less than 1 minute read

HTTPS

简单来说,HTTPS 可以理解为“运行在 TLS 之上的 HTTP”。也就是在 HTTPTCP 之间,再加上一层 TLS 来完成加密、身份认证和完整性保护。

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

HTTPS 使用的是混合加密机制:先通过非对称密码学完成身份认证和密钥协商,之后再使用对称密钥来保护后续通信。

它主要解决的是三个问题:

  1. 防止通信内容被窃听;
  2. 防止传输内容被篡改;
  3. 尽量确认“我现在连的是不是真正的服务器”。

证书

Certification Authority,简称 CA,也就是证书颁发机构。它的作用很简单:充当一个被大家共同信任的“担保人”。

在使用 HTTPS 之前,服务端通常需要先向 CA 申请一份数字证书。证书里会包含证书持有者信息、公钥信息、签发者信息等。服务器在 TLS 握手时把证书发给客户端,客户端再根据证书链去验证它是否可信。

你可以把数字证书理解成“网站的身份证”:它的核心作用不是加密本身,而是证明“这个公钥确实属于这个网站”。

中间人攻击

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

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

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

抽象来看,一般是下面这种流程:

第一步

中间人拦截客户端发出的 Client Hello,然后自己再向服务器发起一份新的 Client Hello

第二步

服务器返回 Server HelloCertificate 等握手消息,中间人收到之后保存下来;

第三步

中间人接下来会分别与服务端、客户端建立两套独立的 TLS 会话。对于服务端来说,它看到的是“一个正常的客户端”;对于客户端来说,它看到的则是“一个看起来可信的服务器”。

在抓包工具(比如 Charles)这类具体场景里,这一步通常就表现为代理分别和两端完成握手。如果服务端启用了双向认证,那么这时候还会涉及客户端证书的转发或配置。

第四步

当这两套会话都建立完成之后,中间人就处在客户端和服务端的正中间:一边解密客户端请求,一边再重新加密发给服务端;返回时也做同样的事情。

4. 如何防止中间人攻击

如何防止中间人攻击,本质上就是在解决“信任”问题:客户端怎么判断自己面对的到底是真实服务器,还是一个中间代理?

域名强校验

域名校验是证书验证里最基础的一步。客户端收到 Server Certificate 后,会检查证书主题和当前访问域名是否匹配。

它当然不等于 SSL Pinning,但也绝不是没用。没有这一步,客户端甚至没法判断“这张证书是不是发给当前域名的”。

SSL Pinning

固定技术(Pinning)主要有两种常见实现:

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

    这种方式有个明显的问题:一旦证书更新,客户端也必须同步更新,否则就会直接失效。

  2. Certificate Public Key Pinning
    证书公钥固定,原理类似,只不过客户端固定的是证书里的公钥,而不是整张证书。这样即使证书续期了,只要公钥不变,客户端也可以继续工作。所以相比整证书固定,它在工程上更灵活一些。现在很多原生 App 也会同时配置多个公钥,用来应对证书轮换。

双向认证

SSL/TLS 本身支持双向认证,也就是服务端不仅要证明“我是我”,还要求客户端也出示自己的证书。服务端会保存签发客户端证书的根证书,客户端则需要预先持有自己的证书,握手时由服务端验证它是不是受信任的客户端。

这种方案的使用场景相对有限,更适合隐私性和安全要求很高的业务。它可以显著提高攻击门槛,但也并不意味着绝对安全:如果客户端环境已经被完全控制,攻击者依然可能借助代理工具、Hook 或证书导入等方式继续对抗。

数字签名

最后说一下数字签名。数字签名的意义在于:只有持有私钥的一方,才能生成合法签名;验证方则可以通过公钥去验证签名是否正确。它既可以证明“消息确实来自某个持有私钥的实体”,也可以校验消息在传输过程中有没有被篡改。

防止 Hook

攻击者如果已经能够完整逆向客户端、注入动态库,或者直接 Hook 掉校验 API 和签名 API,那么问题就已经不再只是“传输链路安全”了,而是客户端执行环境本身被控制了。

这也是为什么我更愿意把 HTTPS、证书校验、Pinning 看成“链路防护”,而不是“万能防护”。一旦客户端本身失守,攻击者就可能从更高层绕过这些校验。

对于 iOS 开发来说,可以继续做一些对抗,比如检测二进制 Headers 里的 Load Commands 是否包含异常动态库、检测越狱环境、检测 Hook 痕迹等等,但这些更多属于客户端安全对抗,而不只是 HTTPS 本身的话题了。

5. 性能损耗

最后简单说一下性能。

HTTPS 相比 HTTP,额外开销主要来自 TLS 握手、证书校验、密钥协商,以及后续加解密过程。早些年这部分成本确实比较明显,但在今天,随着 TLS 1.3、会话复用、硬件加速等机制越来越成熟,这部分开销已经被压低了很多。

所以在现代移动端开发里,HTTPS 的性能问题当然存在,但通常已经不是“要不要上 HTTPS”的阻碍了。绝大多数场景下,安全收益远远大于这一点性能成本。