伯阳的网络笔记(七):HTTPS

因为疫情期间在外当志愿者,晚上回家无聊翻翻网络知识,权当记录了。
初始动笔:2019-03-03
修改时间:2019-04-02

1. HTTPS

简单的说,HTTPS == HTTP + TLS。在 HTTP 层下面, TCP 层上面,加入了一个 TLS 层用于加密。

关于 TLS 握手,可以查看上一篇文章。

HTTPS 采用混合的加密机制,使用公开密钥加密用于传输对称密钥,之后使用对称密钥加密进行通信。

2. 证书

Certification Authority,简称 CA,证书颁发机构,是指我们都信任的证书颁发机构。

我们在使用 HTTPS 之前,需要向 CA 申请一份数字证书,数字证书里有证书持有者、证书持有者的公钥等信息,服务器把证书传输给浏览器,浏览器从证书里取公钥就行了,证书就如身份证一样,可以证明“该公钥对应该网站”。

证书在 TLS 握手过程中可以直接的进行操作。

3. 中间人攻击

中间人攻击是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。

简单的说,就是存在攻击者在请求和响应传输途中,拦截并篡改内容。

我们知道 HTTPS 在最开始会进行 TLS 握手过程,而中间人攻击一般是发生在会话建立之前。

一般是如下的流程:

第一步

中间人拦截客户端的 Client Hello ,获取其中的随机数,然后继续发送 Client Hello 给服务器;

第二步

服务器发出 Server HelloCertificateServer Key ExchangeServer Hello Done,中间人收到消息,并保存;

第三步

中间人发送 ClientCertificateClientKeyExchangeChangeCipherSpecFinished

发送 ClientCertificate,携带真实的客户端证书,可以在 Charles 的 SSL Proxying Setting 里的 Client Certificate 里配置;

发送 ClientKeyExchange ,携带伪造的 Client DH 协商参数;和服务器协商出预主密钥,计算出主密钥1;发送 ChangeCipherSpec ,通知服务器更改密码规范,发送 Finished 验证密钥。

第四步

中间人收到服务器的 Finished 。 收到服务器的 Finished 以后,发送 ServerHello 给客户端,携带伪造的服务器随机数; 发送 ServerCertificate ,携带伪造的服务器证书; 发送 ServerKeyExchange ,携带伪造的服务器 DH 协商参数; 发送 CertificateRequest ,请求客户端证书; 发送 ServerHelloDone ,问候结束。

4. 如何防止中间人攻击

如何防止中间人攻击的,其实也就是如何解决信任的问题,客户端如何判断出是真实的服务器还是中间人呢?

域名强校验

自定义证书验证逻辑,收到Server Certificate,校验证书域名。这是比较初级的保护手段,对于一般的攻击来说明没有什么作用。

SSL Pinning

加密固定技术,包含两种实现:

  1. Certificate Pinning:
    证书固定,也就是把服务器证书固定在客户端里,每一次请求收到服务器证书的时候,对比证书是否一致,一致则认为是安全的,否则认为是不安全的。

    这种方式有个很大的问题,就是证书如果如果更新了,那么客户端必须同步更新证书,需要强升,否则不能使用。

  2. Certificate Public Key Pinning
    证书公钥固定,原理类似,只不过是把服务器证书的公钥固定在客户端,每一次请求收到服务器证书的时候,对比证书的公钥是否一致。尽管服务器证书更新了,依然可以保持公钥不变,所以不受影响。而且现在一般原生 App 都是配置多个公钥,完全可以应对证书替换的情况,所以是现在比较流行的解决方案,而且现在移动端的网络框架基本都支持,比如说 OKHttp、AFNetworking、Alamofire 等等。

双向认证

SSL/TLS 本身支持双向认证,即服务端需要校验客户端证书,服务端存储了签发客户端证书的根证书,客户端证书需要预先内置在原生 App 里,每次请求服务端都会请求客户端证书,来验证是否是收信任的客户端。其实和网银U盾的原理类似。

这种方案使用场景有限,适合隐私性比较强的应用。而且也不能完全避免中间人攻击,现在Charles等等工具都支持设置Client Certificate了,可以在握手的时候,中间人传输设置好的客户端证书给服务器。

数字签名

最后我们说一下数字签名,数字签名是只有消息的发送者才能产生一段字符串,别人无法伪造(除非知知道你的签名算法),这段字符串也是对发送的消息的完整性的一个校验。

防止 Hook

攻击者完全逆向原生 App,在二进制包 Headers 里的Load Command 加入自己的动态库,通过 Hook 的方式,直接 Hook 掉校验 API 和签名的 API ,无论是 Android 还是 iOS ,越狱非越狱,都可以破解。

对于 iOS 开发来说,我们可以检测二进制文件Headers里的LoadCommands是否包含异常的库。

5. 性能损耗

未完待续