近年来,运行Web服务器的首选方案几乎都是Linux+Mysql+Apache+PHP这套开源组合。但从实际部署来看,选择操作系统仍需根据具体需求来定。例如,若要运行Oracle这类大型应用——Oracle在Linux上的支持显然更好,那么Linux自然是最佳选择;如果换用其他系统,比如在FreeBSD上安装Oracle简直就是一场噩梦。但如果是运行普通的网站应用,FreeBSD+Mysql+Apache+PHP的组合就极具吸引力了。对于任何一个网站而言,稳定性和安全性是底线,否则页面何时被人篡改都浑然不觉,或者数据被彻底删除,那后果将不堪设想。毕竟如今各类红客、黑客都虎视眈眈,必须严加防范。
当然,这并不意味着Linux不安全。问题在于Linux下集成了大量可能带来风险的组件,但只要配置得当,Linux同样可以固若金汤。根据中国网络应急响应中心几个月的数据统计:每月被成功入侵的系统中,Linux占比超过六成,Windows占三成多,而FreeBSD的入侵比例仅为百分之几。差距相当显著。
任何操作系统都可以做到很安全,也可能非常脆弱,关键在于管理员如何操盘。世界上没有绝对安全的系统,只有相对更安全的系统。以下内容将介绍如何在FreeBSD上构建一个相对安全的Web服务器,希望为网络安全爱好者提供一些启示,权当抛砖引玉。
一、系统与服务程序的安装
1. 系统安装
从安全角度出发,选择最新的FreeBSD版本是明智之举,不仅兼容性更好,还能规避大量已知漏洞。这里选用的是FreeBSD 5.3,整个安装过程并不复杂,如果不熟悉可以参考FreeBSD中文手册。安装时必须包含基本包和内核源代码,后续编译内核会用到。如果习惯使用ports管理软件,也一并装上,但那些用不上的程序尽量别装。系统文件拷贝完成后会要求配置IP、域名服务器等信息,记得关闭IPv6,不要开启DHCP,系统自带的FTP服务也要禁用。配置 /etc/inetd.conf 时打开SSH以便远程管理——如果不打算使用inetd这个超级服务,也可以在 /etc/rc.conf 中加入 inetd_enable="NO" 并设置 sshd_enable="YES",后面会详细讲解SSH的配置。
系统安装完成后,在 /etc/inetd.conf 中将除了SSH之外的服务全部关闭,telnet、rlogin等更需慎重,否则系统可能很快就会被攻破。紧接着建议用 make world 或 cvsup 升级系统和ports,这和Windows装完系统打补丁是同样的道理。
2. 服务程序安装
系统搞定后,就该安装应用软件了。原则很简单:选用最新版本,因为旧版本往往存在溢出等漏洞。我们需要数据库来支撑Web服务,同时还要有FTP服务以便远程文件管理。当然,为了更方便,也可以安装Webmin,万一没有SSH客户端时能作为替代。
Web服务选择Apache httpd 2.0.53,这是当时的最新版本。网站采用PHP开发,所以安装PHP 4.3.11,同样是最新版。如果网站需要PHP5,可以下载php5.0.4。数据库选用最快的Mysql 4.0.23,如果需要外键、事务、子查询、存储过程等功能,可考虑4.1或5.0。FTP服务选择vsFTPd 2.0.2,这款软件既安全又高效——我在局域网测试过,它的最高传输速率可达10MB/S,而proFTPd只有8MB/S。vsFTPd对小规模FTP服务器支持特别好,用户不多时完全够用。如果用户数量多、功能要求高,可以考虑proFTPd、pure-FTPd或wu-FTPd,不过有些FTPd安全性存疑,选择时需多加留意。
服务器程序列表:
- Apache 2.0.53 下载地址:https://httpd.apache.org
- PHP 4.3.11 下载地址:https://php.net
- Mysql 4.0.23 下载地址:https://dev.mysql.com
- vsFTPd 2.0.2 下载地址:https://vsftpd.beasts.org
始终牢记:最少的服务 + 最少的端口 + 安全的设置 = 最大的安全。用不上的服务(比如telnetd、rlogind)千万别装,装了反而增加风险。安装方式可以手工编译,也可以使用FreeBSD的ports,根据个人喜好选择即可。
二、系统安全设置
1. 用户控制
用户数量越少越好。我们的FTP账户与系统账户绑定,因此添加用户时先创建一个目录,然后将新建用户的主目录指向该目录。比如网站目录在 /usr/www,那就新建一个用户 www_user,主目录设为 /usr/www,同时将其shell设置为 /usr/sbin/nologin,防止通过SSH登录系统。FTP密码必须足够复杂,防止暴力破解。至于root用户的密码,最少也要10位,并且要数字+字母+字符混搭,最好是18位。如果密码过于简单,黑客暴力破解SSH上的root账户,没几天系统就可能失守。建议每月至少更换一次root密码。普通用户原则上不给系统登录权限——把shell设置成/usr/sbin/nologin。
如果需要使用root权限,建议创建一个属于wheel组的小用户,登录后使用su命令提升为root。这样即使黑客拿到了普通用户的权限,也无法直接操作root,相当于多了一道防线。
2. 文件访问控制
有时候黑客拿到低权限用户后,可能会通过WebShell读取 /etc/passwd 和 /etc/master.passwd 的内容,然后尝试破解root密码。因此必须控制关键文件只能由root访问。比如uname、gcc等工具,如果黑客拿到了低权限,就能查看系统版本,然后寻找对应版本的溢出程序,用gcc进行编译。如果我们能限制黑客访问这些程序,就能在一定程度上拖慢入侵的节奏。
使用chmod来改变文件的权限。例如,要让 /etc/passwd 和 /etc/master.passwd 只允许root访问:
# chmod 700 /etc/passwd
# chmod 700 /etc/master.passwd
或者使用字符标记的方式:
# chmod u+w+r+x,go-w-r-x /etc/passwd
# chmod u+w+r+x,go-w-r-x /etc/master.passwd
系统中还有很多重要文件需要控制访问权限,一定要把握好,否则威胁不小。
3. 系统服务和端口控制
端口开得越多,黑客入侵的机会就越多;服务越多,风险越大,因为你无法预知哪些服务暗藏漏洞。所以尽量少开服务。例如sendmail默认是开启的,建议关掉。在 /etc/rc.conf 中添加:
sendmail_enable = "NONE"
如果设为"NO",只能关闭pop3服务,smtp仍在运行。只有设为"NONE"才能彻底关闭。
除了Apache、Mysql、vsFTPd、SSH,系统中最好不要开启其他任何端口和服务。使用 netstat -a 查看打开的端口,然后从端口对应去找相关服务。我们这边应该只开放21、22、80、3306这几个端口。如果有其他端口,务必仔细检查——很可能是黑客的后门或者存在威胁的服务。有些服务不需要对外暴露网络连接,比如Mysql,可以关闭Socket,这个在Mysql安全设置中会讲到。另外,可以通过防火墙控制端口访问,例如让Mysql的3306端口只允许192.168.0.1访问,在ipfw中添加规则:
ipfw add 10001 allow tcp from 192.168.0.1 to 10.10.10.1 80 in
这样就能防止黑客直接攻击Mysql服务。防火墙的具体配置将在后面讲解。
4. 日志管理和控制 (未完)
5. 文件指纹检测
文件指纹包括文件权限、所属用户/组、最后修改日期、大小等信息。黑客入侵后通常会修改文件,文件指纹就会发生变化。文件的MD5校验值也属于文件指纹的一种。
为了防止黑客篡改关键文件(比如 /etc/passwd、/etc/shadow、/etc/inetd.conf等),可以将重要文件备份,同时保留一份当前的文件指纹。例如备份 /etc、/bin、/usr/bin 目录下的文件指纹:
# ls -l /etc > /var/back/etc.txt
# ls -l /bin > /var/back/bin.txt
# ls -l /bin > /var/back/usrbin.txt
当然,也可以给每个重要文件加上MD5校验值。感觉不对劲时就做一次匹配,确保文件安全。哪些目录需要备份,自己根据情况决定。这主要是为了在被入侵后做系统检测和恢复——比如对比 /etc/inetd.conf 与备份文件,查看是否被安装了服务型后门。
6. 系统指纹泄漏和防范 (未完)
黑客入侵前,通常都会先扫描目标系统——包括开放的端口、使用的服务程序和操作系统信息。比如手工检测Web服务的指纹:
# telnet target.com 80
这样很可能返回Apache和PHP的版本信息。同样,扫描工具也能获取Mysql、vsFTPd、SSH等服务的指纹。暴露的信息越多,风险越大。解决办法就是修改所有服务程序的Banner,迷惑黑客。下面简单介绍几种服务的Banner修改方法。
* Apache
修改httpd.conf:
ServerSignature Off
ServerTokens Prod
以上适用于apache 1.x,apache 2.0默认也有效,但仍有server=Apache字样。要彻底去掉,需要重新编译。修改httpd.h:
Include/httpd.h
Define SERVER_BASEVENDOR "Apache Group"
Define SERVER_PRODUCTVENDOR "Apache"
Define SERVER_BASEVERSION "1.3.27"
重新编译Apache后即可完全去除。
* PHP
在php.ini中设置:
expose_php = Off
这样HTTP头信息中就不会包含PHP的版本信息。
* Mysql
(待补充)
* vsFTPd
vsFTPd默认的Banner信息是"Welcome to FTP Server!",高手一看就能猜出个大概。修改vsftpd.conf:
Ftpd_banner=xxxxx
将xxxxx替换成你想要的。
* SSH
FreeBSD下默认安装的SSH在 telnet target.com 22 时,会显示SSH和FreeBSD的信息,这等于把底牌完全亮给黑客。目前尚未找到修改方法,知道的高手请不吝赐教。
7. 系统内核安全
FreeBSD有一个很实用的功能——定义系统内核的安全等级,主要用来防范内核后门。通过不同等级限制对内核的访问以及对防火墙的修改。首先开启安全等级,然后在 /etc/rc.conf 中设置:
kern_securelevel_enable="YES"
kern_securelevel="-1"
第一句是开启安全等级,第二句是定义等级。一共有五个等级:
- kern_securelevel -1:系统默认级别,没有内核保护。
- kern_securelevel 0:基本没什么用。系统刚启动时是0级,进入多用户模式后自动变成1级。
- kern_securelevel 1:限制如下:
- 不能通过kldload或kldunload加载或卸载内核模块;
- 应用程序不能通过/dev/mem或/dev/kmem直接写内存;
- 不能直接往已挂载的磁盘写东西(不能格式化磁盘),但可以通过标准内核接口写;
- 不能启动X-windows,不能使用chflags修改文件属性。
- kern_securelevel 2:在1级基础上,还不能写未挂载的磁盘,且不能在1秒内多次警告(防止DoS控制台)。
- kern_securelevel 3:在2级基础上,不允许修改IPFW防火墙规则。
如果已经装好防火墙、规则固定不变,建议用3级。如果还没装防火墙或准备改规则,不要用3级。推荐使用2级,能防范大部分内核攻击。
8. 系统安全优化
系统优化主要是重新编译内核,去掉不用的驱动。这里重点对网络和内核参数进行优化和安全设置。编辑 /etc/sysctl.conf,加入以下内容:
| 参数 | 说明 |
|---|---|
| net.inet.tcp.sendspace=65536 | 最大的待发送TCP数据缓冲区空间 |
| net.inet.tcp.recvspace=65536 | 最大的接收TCP缓冲区空间 |
| net.inet.udp.sendspace=65535 | 最大的接收UDP缓冲区大小 |
| net.inet.udp.maxdgram=65535 | 最大的发送UDP数据缓冲区大小 |
| net.local.stream.sendspace=65535 | 本地套接字连接的数据发送空间 |
| net.inet.tcp.rfc1323=1 | 加快网络性能的协议 |
| net.inet.tcp.rfc1644=1 | |
| net.inet.tcp.rfc3042=1 | |
| net.inet.tcp.rfc3390=1 | |
| kern.ipc.maxsockbuf=2097152 | 最大的套接字缓冲区 |
| kern.maxfiles=65536 | 系统中允许的最多文件数量 |
| kern.maxfilesperproc=32768 | 每个进程能同时打开的最大文件数 |
| net.inet.tcp.delayed_ack=0 | 是否延迟ACK应答。高速网络/低负载时可略微提高性能;网络差时反而降低性能 |
| net.inet.icmp.drop_redirect=1 | 屏蔽ICMP重定向功能 |
| net.inet.icmp.log_redirect=1 | |
| net.inet.ip.redirect=0 | |
| net.inet6.ip6.redirect=0 | |
| net.inet.icmp.bmcastecho=0 | 防止ICMP广播风暴 |
| net.inet.icmp.maskrepl=0 | |
| net.inet.icmp.icmplim=100 | 限制系统发送ICMP速率 |
| net.inet.icmp.icmplim_output=0 | 安全参数,编译内核时需加options TCP_DROP_SYNFIN |
| net.inet.tcp.drop_synfin=1 | |
| net.inet.tcp.always_keepalive=1 | 清除没有正常断开的TCP连接。适合拨号用户 |
| net.inet.ip.intr_queue_maxlen=1000 | 若看到net.inet.ip.intr_queue_drops增加,则调大此值 |
| net.inet.tcp.msl=7500 | 防止DoS攻击,默认30000 |
| net.inet.tcp.blackhole=2 | 接收到已关闭端口发来的所有包直接drop;设为1则只针对TCP包 |
| net.inet.udp.blackhole=1 | 接收到已关闭端口发来的UDP包直接drop |
| net.inet.tcp.inflight.enable=1 | 为网络数据连接提供缓冲 |
| net.inet.ip.fastforwarding=0 | 打开后会记录路由表,但消耗内核内存 |
| kern.polling.enable (注释) | 编译内核时需打开options POLLING,高负载下使用,SMP不能和polling共存 |
| kern.ipc.somaxconn=32768 | 并发连接数,默认128,推荐1024-4096,越大越占内存 |
| security.bsd.see_other_uids=0 | 禁止用户查看其他用户的进程 |
| kern.securelevel=0 | 设置kernel安全级别 |
| net.inet.tcp.log_in_vain=1 | 记录任何TCP连接 |
| net.inet.udp.log_in_vain=1 | 记录任何UDP连接 |
| net.inet.udp.checksum=1 | 防止不正确的UDP包攻击 |
| net.inet.tcp.syncookies=1 | 防止DoS攻击 |
| kern.ipc.shm_use_phys=1 | 仅为线程提供物理内存支持,需256MB以上内存 |
| kern.ipc.shmmax=67108864 | 线程可使用的最大共享内存 |
| kern.ipc.shmall=32768 | 最大线程数量 |
| kern.coredump=0 | 程序崩溃时不记录 |
| net.local.stream.recvspace=65536 | lo本地数据流接收空间 |
| net.local.dgram.maxdgram=16384 | 本地数据报 |
| net.local.dgram.recvspace=65536 | |
| net.inet.tcp.mssdflt=1460 | 数据包数据段大小,ADSL为1452 |
| net.inet.tcp.inflight_enable=1 | 为网络数据连接提供缓冲 |
| net.inet.tcp.minmss=1460 | 数据包数据段最小值,ADSL为1452 |
| net.inet.raw.maxdgram=65536 | 本地数据最大数量 |
| net.inet.raw.recvspace=65536 | 本地数据流接收空间 |
| net.inet.ip.fw.dyn_max=65535 | ipfw防火墙动态规则数量,默认4096,增大可防病毒大量TCP连接 |
| net.inet.ipf.fr_tcpidletimeout=864000 | ipf防火墙TCP连接空闲保留时间,默认8640000(120小时) |
三、服务程序的安全设置
这一节是重点,需要花些篇幅来讲解。系统默认运行Apache、Mysql、vsFTPd、SSH等服务,下面逐一说明。
1. Apache的安全设置
Apache的核心配置位于 httpd.conf,安装目录是 /usr/local/apache2/,配置文件在 /usr/local/apache2/conf/httpd.conf。使用ee或vi打开:
# ee /usr/local/apache2/conf/httpd.conf
下面重点介绍安全相关的设置,基本服务、端口、主目录这些不再赘述。
(1) 指定运行Apache的用户和组
这非常关键——权限是继承的。如果运行Apache的用户权限过高,入侵者通过WebShell就能对系统造成严重威胁。一般使用nobody用户和nobody组。在httpd.conf中找到User和Group选项:
User nobody
Group #-1
(2) Apache的日志文件
日志文件非常重要,能反映Apache的运行状况和访问情况,对判断入侵很有帮助。默认配置如下:
ErrorLog logs/error_log # 错误日志存放目录
LogLevel warn # 日志级别(debug, info, notice, warn, error, crit)
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
CustomLog logs/access_log common # 使用common格式
预定义的格式内容:
- %a:远程用户IP
- %A:本地httpd服务器IP
- %f:传送的文件名
- %h:远程主机
- %m:请求方式
- %l:identd给出的远程名
- %p:连接的httpd端口
- %P:请求的httpd进程
- %t:时间
- %T:服务请求时间
可以定制自己的日志格式,通过CustomLog调用。注意,日志文件由运行Apache的用户打开,要注意安全性,防止被改写或删除。
(3) Apache服务信息显示控制
配置文件中有个选项控制是否显示Apache版本、主机名、端口、支持的CGI等信息:
ServerSignature On
默认为On,所有信息都会显示。比如故意访问一个不存在的文件:https://target.com/404.html,那么错误提示中会显示——
Apache/2.0.53 (Unix) PHP/4.3.11 Server at target.com Port 80
所有信息都暴露无遗。还有Off和EMail选项,Off不显示任何信息,EMail显示管理员邮箱。建议设为Off或EMail。
(4) 目录浏览
配置中可以设置Apache对没有索引文件的目录进行浏览:
Options Indexes FollowSymLinks
AllowOverride None
这很不安全。如果不需要目录浏览,建议改成:
Options FollowSymLinks
AllowOverride None
(5) 用户主页
设置:
UserDir public_html
这样每个用户都能在自家目录下创建public_html,然后通过 https://target.com/~用户名/ 访问。这对服务器来说既没必要也不安全。直接关闭:
UserDir disabled
或者改名成黑客不容易猜到的文件名,比如:
UserDir webserver_public_htmlpath
也可以只允许部分用户:
UserDir enabled user1 user2 user3
(6) CGI执行目录
如果要执行perl等CGI程序,需要设置:
ScriptAlias /cgi-bin/ "/usr/local/apache/cgi-bin/"
但这也会给黑客留下机会——利用不安全的CGI程序搞破坏。如果不需要,建议注释掉:
#ScriptAlias /cgi-bin/ "/usr/local/apache/cgi-bin/"
(7) 控制PHP脚本只能访问指定目录
在httpd.conf中添加:
php_admin_value open_basedir /usr/www
后面的路径是PHP脚本能访问的目录。如果PHP脚本试图访问其他目录,就会报错。
