HTTPS 真的就安全了么?

Posted by hurshi on 2019.08.04

非对称加密

  1. 公钥是公开的。私钥是私有的。公钥加密只能用私钥解密,私钥加密只能用公钥解密。
  2. 其他人使用我的公钥加密,自己用私钥解密,这叫“加密”。
  3. 自己用私钥加密,其他人使用我的公钥解密,这叫“签名”。
  4. 在 3 的基础上,如果这对非对称密钥是由权威的“证书中心”颁发的,就叫“数字证书”。

数字证书

  1. 证书机构对该证书的数字签名(Signature Algorithm)
  2. 有效时间(Validity)
  3. 证书持有人 的信息(包含网址域名)
  4. 公钥(Public Key)
  5. 其他

为什么说 HTTPS 是安全可靠的?

https 通信机制

TLS - RSA
sequenceDiagram participant C as Client participant S as Server C->>S: Random_C,TLS版本,加密套件 S->>C: Random_S, 加密方式,公钥 C->>C: 验证公钥及域名,生成 Pre_Random C->>S: 使用 Server 公钥加密 Pre_Random Note over C,S: 至此Client和Server
都有Random_C,Random_S,Pre_Random
其中 Pre_Random 使用公钥加密,外界无法得知; Note over C,S: Client和Server各自使用Random_C,Random_S,
Pre_Random生成相同的SessionKey
TLS - ECDHE
  1. DH(Diffie-Hellman) 密钥交换算法

    随机数 a, b 都只存在本地

    sequenceDiagram participant A as Alice participant B as Bob A->>A: 生成 a, g, p 然后计算 A = g^a mod p A->>B: g, p, A B->>B: 生成随机数 b,
    计算 B = g^b mod p B->>A: B A->>A: K = B^a mod p B->>B: K = A^b mod p
  2. 使用 ECDHE 的 TLS 流程

    向前安全(前向保密性):私钥被泄漏了,历史数据是否安全

    sequenceDiagram participant C as Client participant S as Server C->>S: Random_C,TLS版本,加密套件 S->>C: Random_S, 加密方式,公钥 S->>C: DH_S_Params, 加签 C->>C: 验证公钥及域名;生成 Pre_Random C->>S: DH_C_Params Note over C,S: 至此Client和Server
    都有Random_C,
    Random_S,DH_S_Params,DH_C_Params Note over C,S: Client和Server各自使用
    上面的数据生成相同的SessionKey
TLSv1.3
  1. 流程

    sequenceDiagram participant C as Client participant S as Server C->>S: Random_C,Params_C,TLS版本,加密套件 S->>C: Random_S,Params_S,加密方式,公钥 C->>C: 验证公钥及域名,然后生成 Pre_Random Note over C,S: 至此Client和Server
    都有Random_C,Random_S,
    DH_S_Params,DH_C_Params Note over C,S: Client和Server各自使用
    上面的数据生成相同的SessionKey
  2. 如何实现 0-RTT

    使用 PSK - pre_shared_key

    1. TLS 握手结束后,服务器可以发送一个NST(new_session_ticket)的报文给客户端,该报文中记录PSK的值、名字和有效期等信息,双方下一次建立连接可以使用该PSK值作为初始密钥材料。

    2. 因为PSK是从以前建立的安全信道中获得的,只要证明了双方都持有相同的PSK,不再需要证书认证,就可以证明双方的身份,因此,PSK也是一种身份认证机制。

    3. 缺陷:0-RTT的实现有一定的安全缺陷,自身没有抗重放攻击的机制

中间人攻击:窃听数据

因为 随机数3 是使用服务器的公钥加密的,所以中间人得不到 随机数3 的值,因而不知道密钥,中间人窃听失败。

中间人攻击:代理( 欺上瞒下 )

中间人代理的方式,如果有中间人代理了你的服务器,但它其实并没有服务器的数字证书,所以它的证书在客户端这边是校验不通过的:要不就是合法性校验不过(非权威机构颁布),要不就是证书中的域名校验不过。(它不可能从权威机构那里拿到你的域名的数字证书)

总结下:HTTPS 能保证服务器到客户端之间数据的安全性,可靠性。你不用担心你的数据被窃听或者被中间人改过了。

HTTPS 双向认证

  1. 以上的 HTTPS 只是单向的验证。仔细想想,只有客户端验证了数据的合法性,而服务端并没有验证客户端数据的合法性,造成了当“客户端沦陷”的时候,服务端完全不知情的状况。

  2. Https 双向认证的基本原理和上文所讲的单向认证差不多,区别在于:客户端也有数字证书,在建立 https 连接的时候,客户端会把自己的证书以及公钥发给服务器。这样就能保证客户端发给服务端的消息不会被中间人篡改。

HTTPS 真的就安全了么?

  1. 从普通用户角度来说,HTTPS 的确已经够了。
  2. 但对于企业来说,除了服务普通用户之外,还得提防黑客的攻击,HTTPS 能保障你的安全么?显然不会:
    1. 黑客客户端可以安装信任自定义证书,那么你和客户端之间就不是直接连接,而是被“代理”了,而在代理服务器上,你的所有数据都是透明的。Fiddler, Charles, whistle 都是这么实现的。
    2. 某些浏览器支持将 TLS 会话中使用的对称密钥保存在外部文件中,这相当于密钥泄露了。所以黑客完全可以通过这个密钥来解密窃听来的数据。Wireshark 就是这么搞的。可以看看:三种解密 HTTPS 流量的方法介绍
    3. 对于双向认证来说,客户端的“私钥”就安全么?怎么保证不会落入黑客之手?

解决方案

  1. 证书锁定:在客户端判断证书是否是预设的证书,若不是的话就不信任。

    1. 在OkHttp 中可以设置:

      1
      2
      3
      4
      5
      6
      7
      8
      
      val hostName = "www.baidu.com"
      val pin1 = "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
      val pin2 = "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="
      val certificatePinner = CertificatePinner.Builder().add(hostName, pin1, pin2).build();
            
      val okHttpClient = OkHttpClient.Builder()
              .certificatePinner(certificatePinner)
              .build()
      

      这样,就在客户端锁定了服务端的证书 sha256 值必须得是 sha256/AAAA.....才行,其他的证书都不行。以此防止客户端被黑客调试。

    2. 设置证书锁定后,客户端首先还是会和常规 https 一样在系统根证书中去确认本连接的证书的合法性,校验通过后,再去比对 CertificatePinner 中对应域名的 pin 值是否匹配。匹配后才会放行。

    3. 弊端:证书锁定限制了服务器更新证书的能力,就像客户端用 IP 地址类似。

  2. 白盒加密:在客户端使用白盒加密对称加密密钥,然后和服务端之间采用对称加密的 http 传输。(如何防止黑客从内存中获取解密后的密钥呢?)

现在安全了么?

道高一尺魔高一丈,黑客如果还要“搞你”,还是有其他方法的,以上方法并没有高枕无忧。

  1. Frida hook 方案。(需要手机 Root,但对于黑客来说谁没有一只 Root 的手机呢?)
  2. 各种逆向,重打包方案。

参考