2017年4月21日更新:因StartCom和WoSign接连曝出证书签发丑闻,Mozilla, Apple和Google已经决定撤销对这两家CA厂商的信任。Alex Sky网站也于2016年8月31日全部改为Let’s Encrypt CA。使用StartSSL的站长应该尽快转用其他CA。
在上两篇文章中,我们谈到了如何通过网页设计和服务器设置降低页面呈现时间,提高网站整体性能和提高用户体验。这一篇文章将涉足一个新的领域——安全。在我说的网站的四个重要价值——“功能”、“交互”、“性能”、“安全”中,“安全”恐怕是最少提到的话题之一了,对于WordPress更是如此。不过在当下,网站安全的话题却屡屡被提及,从前些年的CSDN用户密码泄露,到最近才发生的adobe账户泄露,再到逐渐发酵的凌镜门事件,网络攻击似乎并没有随着科技的进步而减少,反而愈演愈烈,并逐步替代木马和病毒,成为信息安全中的主要威胁。
作为用户,我们小心谨慎,注意密码安全,关闭浏览器的密码自动填充,更改路由器密码。不过这些远远不够抵御目前的诸多网络攻击。比如前几天我发现新浪微博和人人网仍然无法避免会话劫持(Session Hijacking)的攻击。我认为作为站长,对于此类攻击有不可推卸的责任。
提到网站的安全,我们首先会想到服务器的安全,比如SSH的密码安全,防火墙,SQL注入防护等。这些都非常重要,而且也是建立一个网站必要的措施。不过我今天谈到的一些安全措施,则更加针对当前的网络攻击,尤其是直接针对终端用户的攻击。这类安全措施目前较少受到重视,但却是必不可少的。我将以当前的攻击为主线,来讲讲如何通过合理配置服务器来防护。
Information Scan
在所有网络攻击前,黑客最有可能的操作就是扫描目标网站,找寻漏洞。比如发现SQL注入点,发现未关闭的端口,但更为简单的是读取服务器和PHP的版本号,因为某些版本有现成公开的漏洞可以利用。比如,BlogCN的返回标头中写了Server:nginx/0.7.67,而BlogBus的返回标头中写了X-Powered-By:PHP/5.2.13。这种版本号对于页面显示没有任何用处,反而会给黑客攻击带来重要的参考。所以最简单的方法就是关掉显示。
在php.ini中
expose_php = 0
在apache2.conf中
ServerSignature Off
ServerTokens Prod
这样一来,黑客并不知道你使用的软件版本是什么,也就只能采用其他扫描的手段,攻击的门槛也相应提高了。
XSS攻击
XSS攻击是当前最常见的攻击手段之一。XSS全称叫跨站脚本(Cross-Site Scripting),是指一个网站引入了存储在另一个网站的脚本,因为浏览器对于脚本一视同仁,所以都会执行,从而造成威胁。我们拿新浪微博作为例子讲。
新浪微博weibo.com是一个正常的网站。黑客网站hack.com是黑客用来存放脚本的网站。以下是可能的攻击过程。
- 黑客在weibo.com上找到一个程序漏洞,这个漏洞让他能将任意脚本注入到页面里,让其他人访问
- 黑客通过发布精心撰写的帖子,将脚本语句注入到页面
- 黑客的关注者看到了这个帖子,触发了该脚本
- 该脚本将weibo.com页面上的姓名等个人信息发送至hack.com
- 该脚本逐个读取这个访客自己的关注者,并将他们的个人信息发送到hack.com
- 该脚本指示浏览器在weibo.com发送帖子,从而将该脚本继续转发到该访客的关注者的页面上,扩散开来
- 该脚本伪造隐性的登陆密码框,获取开启了密码自动填充的浏览器保存的密码,并发送至hack.com
- 对于没有开启自动填充的浏览器,该脚本伪造一个可见的密码登录框,诱使用户输入密码,再将该密码发送至hack.com
- 该脚本自动删除访客页面上的该帖子,销声匿迹
以上的9个步骤,最短可以在2-3秒内完成,而访客完全无法察觉。如果你觉得这不可能,那我告诉你,2011年,类似的攻击已经在新浪微博出现。而现代浏览器对于此类攻击的防护是0,就算装了再强悍的杀毒软件也没有用。
XSS发生的根源是浏览器对于用户访问的页面有绝对的操控权,而它对于这个页面传递的所有信息都是默认信任的。
那么有没有办法防范呢?当然有,作为用户我们可以安装NoScript等插件,但是对于普通用户,这类插件非常难驾驭,因此站长应该肩负起防御XSS的责任。幸运的是,现代浏览器已经开始重视这一问题,并且实施了一系列XSS保护措施,为了防止用户关掉XSS保护,网站可以发送一个标头来强制执行XSS保护。
X-XSS-Protection:1; mode=block
这样就告诉浏览器一定开启XSS保护。不过,浏览器自带的XSS防护是针对所有网站的,为了兼容性有所保守。比如,浏览器可能会阻止跨站的请求,比如上述步骤中的4、5、7、8,但对于在网站内的行为浏览器大多不会阻止。这时就需要更严格的规范了。而这个规范就是“内容安全策略”(Content Security Policy)。
CSP是对XSS和其他安全威胁的一种有力的防范,由Mozilla于2009年推出,经过这些年不断完善,已经于去年成为W3C的candidate recommendation,已经被大多数浏览器所支持。CSP规定了一系列资源请求限制,从而约束浏览器不下载执行信任区外的脚本和资源。
内容安全策略通过Content-Security-Policy这个标头通知浏览器,每一条规则由分号分开。下面以Alex Sky的CSP规则为例,逐一介绍具体的规则用法。
default-src 'self'
这条规则是CSP的默认规则,即其他规则如没有限制则参照这条规则。这条规则限定了页面能载入的所有资源仅限当前的域名。这些资源包括脚本、图片、样式表、字体等等。如果需要更宽松的限制,则在之后的具体规则中排除。
script-src 'self' 'unsafe-inline' 'unsafe-eval' hm.baidu.com
这条规则定义了页面能载入的脚本的来源。在这里页面能载入的脚本来自于当前的域名、hm.baidu.com(百度统计)、页面当中的脚本和动态脚本。如果黑客实现注入,除非修改服务器设置,否则浏览器也不会载入存放在hack.com的恶意脚本。这条规则从根本上杜绝了XSS。
style-src 'self' 'unsafe-inline'
这条规则定义了页面能载入的样式表的来源。在这里页面仅能载入当前域名的样式表和内嵌在页面中的样式。这防止了黑客通过载入其他样式表篡改网页的呈现形式。
img-src 'self' data: http://bcs.duapp.com http://bcs.duapp.com/alexsky/ hm.baidu.com
这条规则定义了页面能载入的图像的来源。在这里页面仅能载入当前域名的图片和来自bcs.duapp.com(百度云存储)以及hm.baidu.com(百度统计)的图片。注意bcs.duapp.com前面加上了http://,这是因为CSP默认仅载入相同协议的资源,比如如果访问SSL的Alex Sky,则无法载入未加密的http://hm.baidu.com的内容,但因为百度云存储不支持SSL,所以加上例外。至于后面的alexsky路径,则是为了限制页面不要载入其他人创建的云存储的内容,但这种基于路径的规则在CSP1.0中暂不支持,所以目前的FireFox不支持这条语法,不过Chrome可以支持。
object-src 'none'
这条规则定义了页面能载入的插件的来源。因为Alex Sky仅使用HTML5播放音频视频,所以不需要引入额外的flash,所以在这里页面不允许任何插件加载,这样可以防止黑客调用用户浏览器中有漏洞的插件实现进一步入侵。
frame-src akismet.com
这条规则定义了页面能够载入的框架的来源。在这里页面仅能载入akismet.com的框架内容,这是为了方便查看垃圾评论的统计数据。禁止框架载入,能够防止黑客利用框架载入hack.com的各种资源,入侵用户电脑。
media-src 'self' http://bcs.duapp.com http://bcs.duapp.com/alexsky/
这条规则定义了页面能够载入的媒体的来源。媒体包括<audio>,<video>,<source>和<track>标签。在这里页面仅能载入当前域名以及http://bcs.duapp.com/alexsky/(自有百度云存储)的视频和音频内容。当前有些人利用audio和video标签来载入恶意脚本或者进行CSRF攻击(后文会提到),在这条规则下面就无法进行了。
connect-src 'self' hm.baidu.com
这条规则定义了页面能够连接的地址。这里主要针对的是XMLHttpRequest,WebSocket和EventSource。在这里页面仅能向当前域名和hm.baidu.com(百度统计)发送请求,其他域名的请求一概阻止,从而彻底阻止信息泄露。
form-action 'self'
这条规则定义了表单提交的地址。表单提交的规则并不能集成默认规则,所以需要单独制订。这里页面仅能向当前域名提交表单,从而防止黑客伪造表单,进行钓鱼攻击。
plugin-types 'none'
这条规则定义了允许的插件类型。与之前的object-src不同的是,这条规则不是针对插件来源,而是插件类型。在这里页面不允许载入任何插件。这样即使你的网站本身带有flash插件文件,黑客也不能调用该插件来进行入侵。这条规则还防止了“挂羊头卖狗肉”的入侵方法。
reflected-xss block
这条规则同上面的X-XSS-Protection一样,用于激活浏览器自身的XSS防护功能。只不过X-XSS-Protection是IE专有的标头,而这条规则是W3C规范。
综上,我们将规则写成一个标头的形式如下。
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' hm.baidu.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: http://bcs.duapp.com http://bcs.duapp.com/alexsky/ hm.baidu.com; object-src 'none'; frame-src akismet.com; media-src 'self' http://bcs.duapp.com http://bcs.duapp.com/alexsky; connect-src 'self' hm.baidu.com; form-action 'self'; plugin-types 'none'; reflected-xss block"
这样一来,浏览器就会根据以上的规则来处理页面中的所有请求,从根本上杜绝XSS在该网站的使用。关于Content Security Policy的更详细说明,请参照W3C官方文档。
CSRF攻击
CSRF攻击近年来也十分流行。CSRF全称是“Cross-Site Request Forgery”,即“跨站请求伪造”。同上文提到的XSS不同,CSRF利用了浏览器对于一个站点的历史信任,这种信任往往通过cookie的方式建立。攻击的方法既可以通过XSS在正常网站实现,也可以通过诱导用户访问黑客网站来实现。
- 用户访问有问题的网页hack.com,这个网页里面有一个img,video,audio等标签,来源是一个用户曾经访问过的网站weibo.com
- 浏览器为了读取图片,访问了weibo.com,可是这条URL并不指向一个图片地址,而是weibo.com/delete.php?post=allpost(假想网址),而这条地址的作用就是删除所有已发帖子
- weibo.com收到这条请求,因为浏览器发送请求的时候自动携带了weibo.com的cookie,所以验证通过,weibo.com删除了该用户的所有帖子
当然,这一切都在用户毫不知情的情况下发生。可以看到,相比于XSS,CSRF攻击更加简明,一条恰当的请求,就能完成这次攻击。
防御CSRF,从用户角度需要安装NoScript等插件,并阻止跨站的POST请求。不过更有效的方法是在服务器端。目前比较流行的防御方法有三种。
- 验证Referer标头,因为目前的主流浏览器都不允许任何人修改Referer标头,所以验证Referer标头能够确保请求来自于信任的页面。不过有一些老式浏览器如IE6可以修改Referer,这就需要我们网站管理员避免使用IE6等老式浏览器登陆任何网站。
- URL token,通过设置URL token来唯一确定用户。但如果黑客知道了token的算法,则该方法无效。
- 自定义Header token,只适用于XMLHttpRequest,实施难度大,局限性较大。
这三种方法中,第一种实施最简单,而且幸运的是WordPress已经帮我们实现,所以不需要做额外防护。上文提到的CSP中的connect-src规则,可以防御黑客通过XSS操纵我们的终端用户访问其他网站。而站长只要注意不要使用过时的浏览器登陆自己的WordPress,就能防御CSRF。关于CSRF攻击与防御的更多细节,可以参考这篇文章和这篇文章。
点击劫持
点击劫持(Clickjacking)是2008年开始的一种新型的Web攻击方式。黑客诱导用户点击某一个按钮,但在该按钮处内嵌了目标网站的一个框架。当用户点击该按钮时,其实点击的是目标网站的目标按钮。具体攻击方法如下。
- 黑客在其控制的网站hack.com上制作一个小游戏,有一个按钮是开始按钮
- 黑客在hack.com页面上用框架形式载入正常网站weibo.com
- 黑客通过调整CSS让weibo.com中的发布按钮与hack.com上的开始按钮在完全相同的位置上
- 黑客调整CSS让该框架完全透明,并处于最上层
- 用户点击开始按钮,实际点击的是weibo.com中的发布按钮
以上例子中只是诱使用户发布一条帖子,在真实的攻击中,黑客曾劫持用户通过Flash开启了摄像头,而用户完全不知晓。
为了防御点击劫持,浏览器会在网站的指示下,禁止载入特定的网站。而这个指示是通过一个返回标头实现的。
X-Frame-Options: DENY
这个标头告诉浏览器不要将该网页在frame或iframe框架中载入,从而避免了黑客进一步劫持用户点击按钮。如果不是DENY而是SAMEORIGIN,则允许网页在当前域名的页面中以框架形式载入。
关于Clickjacking的更多内容和防御,可以参考这篇文章、这篇文章以及这篇文章。
会话劫持
会话劫持(Session Hijacking)是一种古老但近年来流行的攻击手段。黑客在目标用户所处的同一网络进行侦听,记录用户和网站的明文通信,截取cookie,并用该cookie直接登陆目标用户使用的网站。
会话劫持影响的范围非常广泛,所有使用80端口(http://)的网站都是潜在目标,而适用的网络包括有线和无线网。2010年Firesheep的发布,让会话劫持的门槛大大降低,风险大增。公开的WIFI,WEP,WPA/WPA2-PSK,对于会话劫持没有防护。唯一不会被攻破的WPA/WPA2-Enterprise适用范围小。而据我实测,新浪微博和人人网对会话劫持没有任何防护。换句话说,在公众场所浏览这些网站,等于将自己的隐私公开告诉附近的人。
从用户的角度,几乎没有好的防范方法,除非不在公共场所上网或使用VPN。但作为站长,我们应该为用户的隐私安全负责。从技术层面,防范会话劫持最根本和最简单的方法就是使用SSL/TLS加密通讯。对于WordPress来说,我们主要需要保护的就是我们自己,也就是说我们登陆和管理自己的网站,需要加密通讯。
SSL/TLS需要花钱?不需要,对于我们个人站长来说,开启这种安全不需要花费任何钱。以下是步骤。
- 申请一个证书,极力推荐Let’s Encrypt,免费而且可以认证。申请方法见这篇文章。
- 配置Apache或者Nginx,对于Apache可以将自己的site configuration文件拷贝一份,命名为yoursite.ssl.conf,然后打开修改。将端口从80改成443,然后在VirtualHost中加上如下的设置。
SSLEngine on
SSLProtocol ALL -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM
SSLCertificateFile /etc/apache2/ssl/ssl.crt
SSLCertificateKeyFile /etc/apache2/ssl/private.key
SSLCertificateChainFile /etc/apache2/ssl/sub.class1.server.ca.pem
- 最后别忘了开启SSL模块,然后重启Apache
sudo a2enmod ssl
sudo service apache2 restart
这样我们就为网站开启了SSL/TLS。不过,默认状态下,如果在浏览器地址栏中输入网址,浏览器默认会先连接HTTP,这样登陆仍然无法做到通讯加密。因此第二步需要启用WordPress安全管理。启用方法很简单,在wp-config.php里面加上一句。
define('FORCE_SSL_ADMIN', true);
如果按照我上篇文章的方法设置了Varnish缓存,还需要加上一句。
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
$_SERVER['HTTPS']='on';
这样WordPress就会将Admin区域保护起来,建立secure cookie,只有当确定SSL/TLS连接建立时才会发送secure cookie。
如果连普通的cookie都不想泄露,实现完整地加密通讯,可以发送HSTS指令让浏览器今后自动切换成SSL/TLS访问你的网站。方法是在站点配置中加入以下内容。
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
这样一来,在接下来很长时间内,浏览器将仅允许SSL/TLS通讯。该项设置适用于Chrome和FireFox。通过设置SSL/TLS,我们就能较好地防护会话劫持。不过近年来,道高一尺魔高一丈,各种针对SSL/TLS协议本身的攻击开始出现。
BEAST攻击
BEAST攻击,全称“Browser Exploit Against SSL/TLS”,是一种针对SSL3.0/TLS1.0使用的Block Cipher的暴力破解,由Thai Duong与Juliano Rizzo于2011年发布。该攻击通过XSS注入脚本启动Java Applet,然后通过不断尝试新的请求来最终破解加密。抵御BEAST的方法是采用TLS1.1和TLS1.2,不过这两种协议目前的支持范围还不多,另一种方法是禁用Block Cipher而采用Stream Cipher比如RC4。不过RC4在后来的攻击中也被证明有漏洞。幸运的是,目前的主流浏览器已经能够免疫BEAST攻击,所以不用太担心这个攻击
CRIME攻击
CRIME攻击,全称“Compression Ratio Info-leak Made Easy”,是一种针对SSL/TLS传输压缩的暴力破解,同样由Thai Duong与Juliano Rizzo于2012年发布。该攻击通过尝试向服务器发送不同的请求,并嗅探返回内容的大小从而判断cookie的内容。抵御方法是禁用SSL/TLS压缩。幸运的是,目前的主流浏览器已经能够免疫CRIME攻击,所以也不用太担心。
BREACH攻击
BREACH攻击,全称“Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext”,于2013年推出,是CRIME攻击的升级版,攻击方法和CRIME相同,不同的是BREACH利用的不是SSL/TLS压缩,而是HTTP压缩。所以要抵御BREACH攻击必须禁用HTTP压缩。而这在当下看来是弊大于利,因为HTTP压缩是性能优化的重要手段。所以到目前为止,我们还没有什么能够防范BREACH攻击的有效方法。还好BREACH的前提是XSS注入,而这一点可以用之前提到的CSP来抵御。
除了以上几个比较著名的针对SSL/TLS的攻击外,还有一些其他攻击,不过我们没有太多好担心的,因为这些攻击目前只是在理论阶段,要实施起来需要其他攻击手段,如XSS何CSRF的配合,而这类传统攻击手段我们目前都能有效防御。
不过,最近NSA的监控门闹得沸沸扬扬,我们担心如果有类似NSA这样的机构监视了所有加密的通讯,如果在未来某一天他们破解了密匙,就能顺利读取之前的加密信息。因此安全人员提出了Perfect Forward Secrecy(PFS)的概念。
PFS是指采用特殊的cipher suite,保护session key,即使long-term key被截获,也无法推导出session key,从而彻底保护之前的通讯记录。目前只有两种Cipher Suite实现了PFS加密——DHE(Diffie–Hellman Exchange)以及升级版的ECDHE(Elliptic curve Diffie–Hellman Exchange)。具体的细节可以参考这篇文章。目前版本的Chrome和Firefox已经支持ECDHE,而大多数主流浏览器已经支持DHE。不过DHE对于SSL性能的影响比较大,所以应尽量升级到ECDHE。实现ECDHE需要OpenSSH 1.0以上,Nginx,或者Apache2.4。Apache2.2只能支持DHE。
可以通过指定Cipher Suite的顺序来实现PFS,在Apache的站点设置里添加如下内容。
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 \
EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 \
EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"
更新:Nginx的Cipher Suites设置如下:
ssl_ciphers "ECDH+aRSA+AESGCM ECDH+aRSA+SHA384 ECDH+aRSA+AES256+SHA ECDH+aRSA+AES128+SHA DH+aRSA+AES256+SHA";
Nginx的配置文件中,我取消了对Windows XP平台IE6和IE8的支持,因为随着微软取消对Windows XP的支持,这个平台会变得越来越不安全。此外Java6也是不支持的。
因为WinXP下的IE6-IE8不支持PFS,所以只能退回RSA_RC4的方法。参考这篇文章。
想知道自己网站的SSL/TLS设置是否安全,可以通过SSLLab的网站进行测试。测试地址为:https://www.ssllabs.com/ssltest/。Alex Sky的测试结果见此处,可供参考。以下为结果截图:
到此为止,我已经介绍了许多方法防范目前主流的网络攻击威胁。通过这些方法我们可以让网站更加安全,同时更加保障用户的安全和隐私。这篇文章由于篇幅限制,无法包括所有的攻击种类,也还未涉及到WordPress本身的安全性。关于WordPress本身的安全性,可以参考官方提供的文章《Hardening WordPress》,这是我觉得最完整和权威的WordPress安全指南。安全是永无止尽的追求,欢迎大家提供想法,让我们一起打造安全的网络环境。