安全
DANGER
该部分不会持续维护。
说明
- 形式:尽量口语化的笔记。
- 适用:想要了解网络安全的开发者。
- 目标:了解开发和日常使用里常见的安全问题。
从 HTTP 到 HTTPS
HTTP
在信息安全逐步受到重视的大前提下,人们重新审视了 HTTP 协议。
HTTP 通信使用明文
基于 TCP/IP 的 HTTP 本身没有加密的功能,所以没有办法加密通信通道,这是 HTTP 通信使用明文的核心痛点。
而为了防止被窃听,我们引入了加密技术。当然,我们现在知道被广泛使用的就是 HTTPS(HTTP Secure,超文本传输安全协议),我们现在可以简单地认为 HTTPS = HTTP + SSL(Secure Socket Layer,安全套接层)/TLS(Transport Layer Security,安全层传输协议)。HTTPS 组合使用 HTTP 和 SSL/TLS,加密了通信通道,使得别人无法窃听通信内容。
如果我们不加密通信通道,我们也可以加密通信内容。但这么做,别人仍然能窃听加密后的通信内容,所以仍然有通信内容被篡改的风险。
TLS 是以 SSL 为原型开发的协议,后面将统一称为 SSL。
HTTP 协议没有验证通信方的身份
HTTP 不会确认通信方的请求/响应,也就是说任何人都能发出请求/响应,这也就带来了一系列问题。
比较常见的隐患有这么几个:
- 没有办法确认服务器是不是目标服务器,服务器有可能是恶意服务器。
- 没有办法确认客户端是不是目标客户端,客户端有可能是恶意客户端。
- 没有办法确认客户端是不是有权限访问特定的内容。
- 没有办法阻止海量请求下的 DoS(Denial of Service,拒绝服务)攻击。
SSL 提供了证书这种手段来确认身份。一般来说,证书由值得信赖的第三方机构颁发,它非常难以伪造,用于确认身份。所以,只要确认证书,就可以确认持有证书的所有者身份。
HTTP 协议不能证明报文完整正确
HTTP 没有办法确认发出的请求/响应和收到的请求/响应是相同的,所以请求/响应可能会在中途被他人拦截并修改,这也就是中间人攻击(Man-in-the-Middle Attack,MITM)。
可以使用 MD5/SHA-1 等散列值校验的方法、PGP 等数字签名方法来确定完整性,但是需要用户亲自验证,并不便捷,另外也存在 PGP/MD5/... 本身被改写的极端情况,并不安全。
SSL 提供了认证和加密处理及摘要功能,有效地解决了这个问题。
HTTPS
组合使用 HTTP 和通信加密、证书、完整性保护等机制,我们就得到了 HTTPS。
需要再次强调的是,HTTPS 不是应用层的新协议,只是用 SSL/TLS 代替了 HTTP 通信接口部分而已。使用 HTTP 时,HTTP 直接和 TCP/IP 通信。而使用 HTTPS 时,HTTPS 先和 SSL/TLS 通信,再由 SSL/TLS 和 TCP/IP 通信。所以我们上面提到,可以简单地认为 HTTPS = HTTP + SSL/TLS。
HTTPS 不是绝对安全的,但它大幅增加了攻击的成本,所以一般认为 HTTPS 是相对安全的。
对称加密和非对称加密
SSL 使用到了非对称加密,这是一种比对称加密更稳妥的加密方式。
对称加密是加解密共用一个密钥的加密方式,算法公开、计算量小、加密速度快、加密效率高,常见的对称加密算法有 DES,RC,BlowFish。对称加密的问题在于:
- 发送的过程中密钥有可能会被他人窃听,进而丧失加密的意义,但不发送密钥的话对方又无法解密信息。
- 保留密钥需要考虑安全问题。
- 更进一步地说,如果密钥能够安全地发出,那信息也应该能安全地发出,那加密还有什么必要呢?
非对称加密解决了对称加密的几个问题。非对称加密使用了两个密钥:
- 一个称为私钥,请求方自己保留,用于解密响应方返回的已加密的响应信息。
- 另一个称为公钥,请求方公开,响应方用公钥加密响应信息再返回。
这么做就无需担心密钥被窃听进而被用于破解信息,因为发出的密钥只是公钥,而使用公钥成功解密信息的可能性微乎其微,解密成本大大提高,通信双方只需互换公钥就可以实现相对意义上的安全通信。常见的非对称加密算法有 RSA,Elgamal,DiffieHellman,ECC。
非对称加密最大的缺点就是处理速度慢,如果所有通信都使用非对称加密,速度将会大大降低。所以从安全和速度两方面考虑,HTTPS 采用了混合加密机制。
混合加密机制
HTTPS 首先会使用非对称加密方式确定共用密钥,然后在通信过程中使用共用密钥进行对称加密。
由于共用密钥被非对称加密后再进行传输,所以即使被人窃听,也难以解密出共用密钥,后续过程再使用共用密钥加密信息、发送信息就相对而言是安全的。
证书和数字签名
混合加密机制也并非无懈可击,破绽就在于公钥上,怎么证明拿到的用于非对称加密的公钥是真的呢?也就是说,怎么证明正在通信的服务器就是目标服务器呢?
这就需要数字证书认证机构(CA,Certificate Authority)和相关机构颁发的服务器公钥证书了。客户端和服务器都认可并信赖 CA,这是服务器公钥证书起作用的前提。
服务器的运营人员会向 CA 申请公钥证书,CA 判明身份无误后,会对服务器公钥做处理,然后把处理后的服务器公钥和证书绑定到一起,给到运营人员手上。
运营人员配置好服务器公钥证书后,服务器会把这个证书发送给客户端以进行非对称加密通信,收到证书的客户端会使用 CA 的公钥验证证书上的数字签名,确认无误后就能确定两件事情:
- 证书真实有效。
- 服务器的公钥真实有效。
但这里还有一个套娃问题,怎么确定 CA 的公钥真实有效?服务器的公钥由 CA 证书证明真实性和有效性,那 CA 的公钥呢?事实上,客户端基本都内置了常见 CA 的公钥,这一点无需过多担心。
这里另外再说一下,CA 对服务器公钥做的处理,就是数字签名。CA 会使用 CA 私钥对服务器公钥进行签名,使用 CA 公钥就可以验证这个签名。
上面提到的证书,是证明公钥真实有效的证书,但证书不只有这一种,我们还可能会见到证明组织真实性的 EV SSL 证书,用于确认服务器背后运营的企业是不是真的存在。这种证书的目的是防止钓鱼攻击,但实际上并没有起到很好的效果。
除了服务器有证书,客户端也可以有证书,作用和服务器证书一样,证明正在通信的客户端就是目标客户端。但客户端证书应用很少,一般会用于银行业,主要有以下几个问题:
- 用户需要手动安装客户端证书。
- 客户端证书大多数需要付费购买。
- 客户端证书只能证明客户端真实存在,不能证明使用者真实有效。
还有一个比较常见的证书,那就是自签名证书,也就是自己给自己颁发的证书。这种证书多用于苹果设备需要使用特殊工具时,比如爱思助手。只要安装了自签名证书,苹果设备就会认可、允许安装应用。
最后还需要强调的一点是,服务器证书生效的前提是 CA 值得信赖。如果 CA 被入侵,证书就变成了黑客的通行证。尽管还存在着 CRL(Certificate Revocation List,证书吊销列表)和 RCA(Root Certificate Authority,根数字证书认证机构,客户端可以删除根数字证书颁发机构使得对应的数字证书无效)机制作为应对,但生效也需要一段时间,而在这段时间内,黑客还是可以搞一些破坏的。
HTTPS 安全通信机制
在通信开始时,客户端和服务器会进行 SSL 四次握手(注意,三次握手指 TCP 三次握手)。
第一次握手指客户端发送 Client Hello 报文开始通信,报文内包括客户端支持的 SSL 版本,使用的加密算法和密钥长度等信息。
第二次握手指服务器端收到 Client Hello 报文后,以 Server Hello 报文响应,报文内容和 Client Hello 报文内容类似。然后,服务器端发送包含公钥证书的 Certificate 报文。最后,服务器端发出 Server Hello Done 报文,表示传输结束。
第三次握手指客户端发送 Client Key Exchange 报文,报文内包含 Pre-master secret 随机密码串,报文本身使用服务器公钥加密。然后,客户端发送 Change Cipher Spec 报文,目的是提示服务器后续通信会采用 Pre-master secret 密钥加密。最后,客户端发送 Finished 报文,报文内包含连接到现在全部报文的整体校验值,握手能否成功要以服务器能否正确解密为标准。
第四次握手指服务器端发送 Change Cipher Spec 报文和 Finished 报文。
至此,SSL 连接建立完成,通信将受到 SSL 的保护。之后就开始使用应用层协议通信,即 HTTP 通信。
应用层发送数据时,还会附加 MAC(Message Authentication Code)报文摘要,它可以查知报文是否被篡改。
最后还需要明确一点,协议本身几乎不会成为网络攻击的目标,服务器、客户端等才是网络攻击的目标。
XSS
XSS 即 Cross-Site Scripting,跨站脚本。因为 CSS 用于指代 Cascading Style Sheets,层叠样式表,所以跨站脚本的英文缩写使用 X 替代 C 避免混淆。
XSS 攻击常常利用动态创建的 HTML 发起,它可以让攻击者嵌入恶意脚本代码到正常用户会访问到的页面中(比如注册时起名叫 <script></script>
并在其中填入恶意代码),当正常用户访问该页面时,嵌入的恶意脚本代码就会被执行。XSS 攻击会利用虚假表单骗取个人信息,利用脚本窃取用户 Cookie,利用用户发送恶意请求,显示伪造的文章/图片等。
简而言之,如果把输入内容(不只是用户输入内容,也可能是 URL 参数等)直接显示到网页,就很容易遭受 XSS 攻击,所以我们需要转义用户输入的内容乃至 URL 里面的参数,把 <script>
之类的标签转换成 <script>
之类的标签。网站开发的一个准则就是,永远不要相信输入内容,永远要检验输入内容。
同样的解决方案也可以用于 SQL 注入,一种利用不经校验就直接把传递的参数使用到 SQL 语句里的攻击方式。执行了错误的 SQL 语句将可能使数据表被篡改,从而让用户遭受损失。
CSRF/XSRF
CSRF/XSRF 即 Cross-Site Request Forgery,跨站请求伪造。
XSS 攻击更多的是欺骗用户,让用户认为某个网站是安全的,但实际上网站本身会运行恶意脚本。而 CSRF 攻击是欺骗用户浏览器,让浏览器完成恶意操作的攻击。
举个例子,假如一家银行使用特定格式的 URL 地址转账 http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
。攻击者只需要在用户身份信息未过期时添加 <img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">
,就可以成功转账。这意味着,用户只要访问由其他用户生成内容的网站(比如博客),都是不安全的。
目前,浏览器厂商们已经尽力防范 CSRF 攻击,比如逐步禁止第三方 Cookie 等。我们也可以在开发中尽量避免 CSRF 攻击,比如使用 localStorage 和 sessionStorage 保存身份信息(目前常常使用 Token)并在请求时提交,在允许的前提下缩短身份信息的有效时间,人工验证等。
文件上传漏洞
攻击者上传可执行文件、恶意脚本等,随后再执行攻击操作,这就是文件上传漏洞。要避开文件上传漏洞相当简单,只需要在上传时检查文件名称和后缀,同时要求用户使用较新的浏览器版本操作即可。
参考
- 上野宣 - 图解 HTTP
- 一次安全可靠的通信——HTTPS 原理
致谢
- XuXianTao - 阅读了初稿并提供了弥足珍贵的建议和意见