HTTPS简介及防中间人攻击措施

2019/05/10 Java

HTTPS

HTTP缺陷

  • 通信使用明文(不加密),内容可能会被窃听
    • 无法将整个通讯线路加密,通过抓包和嗅探工具(例如wireshark)能够查看到报文的全部内容
  • 不验证通信方的身份,因此有可能遭遇伪装
    • 无法确定请求发送至目标的Web服务器是否是按真实意图返回响应的那台服务器。有可能是已伪装的Web服务器
    • 无法确定响应返回到的客户端是否是按真实意图接收响应的那个客户端。有可能是已伪装的客户端
    • 无法确定正在通信的对方是否具备访问权限。因为某些Web服务器上保存着重要的信息,只想发给特定用户通信的权限
    • 无法判定请求是来自何方、出自谁手
    • 即使是无意义的请求也会照单全收。无法阻止海量请求下的DoS攻击(Denial of Service,拒绝服务攻击)
  • 无法证明报文的完整性,所以有可能已遭篡改
    • 即时请求或响应的内容遭到篡改也没有办法获悉,HTTP虽有确定完整性的方法,但并不可靠,常用的MD5和SHA-1等散列值校验等方法,有可能校验值本身也被修改

HTTPS=HTTP+加密+认证+完整性

HTTPS是包含了HTTP协议及SSL/TLS协议这两部分内容,简单的理解就是基于SSL/TLS进行HTTP的加密传输。

  • 利用SSL/TSL建立加密传输通道
  • 使用CA机构颁发的证书可以对客户端和服务端认证,证书分为(DV SSL 、 OV SSL 、EV SSL)
  • 应用层发送数据会附加叫做MAC(Message Authentication Code)的报文摘要。MAC能够查知报文是否遭到篡改,从而保护报文完整性

HTTPS完整的SSL/TSL握手过程

握手核心要点:

  • 用非对称加密的手段传递密钥,然后用密钥进行对称加密传递数据
  • 握手过程中最重要的就是证书校验,其他就是正常的数据交互过程
  • 如何校验一个证书合法主要包括以下几个方面
  • 第一,校验证书是否是由客户端中“受信任的根证书颁发机构”颁发
  • 第二,校验证书是否在上级证书的吊销列表
  • 第三,校验证书是否过期
  • 第四,校验证书域名是否一致

中间人攻击

用Charles抓到app发起的HTTPS的包是为什么? 针对SSL的中间人攻击过程:

  • 抓包核心是如何欺骗客户端,从而让客户端能够放心的与中间人进行数据交互而没有任何察觉。我们来看Charles如何做到HTTPS抓包最核心的就是:将私有CA签发的数字证书安装到手机中并且作为受信任证书保存

App中防中间人攻击

1. 把签发我们服务器的证书的根证书导出打包到APP中,我们在做服务端证书校验的时候只比对是否和这个证书完全相同

OkHttp设置

public static SSLSocketFactory getSSLSocketFactory(InputStream... certificates) {
        try {
//用我们的证书创建一个keystore
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = "server"+Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
                try {
                    if (certificate != null) {
                        certificate.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
//创建一个trustmanager,只信任我们创建的keystore
            SSLContext sslContext = SSLContext.getInstance("TLS");
            TrustManagerFactory trustManagerFactory =
                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            sslContext.init(
                    null,
                    trustManagerFactory.getTrustManagers(),
                    new SecureRandom()
            );
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
public interface X509TrustManager extends TrustManager {
    void checkClientTrusted(X509Certificate[] var1, String var2) throws CertificateException;

    void checkServerTrusted(X509Certificate[] var1, String var2) throws CertificateException;

    X509Certificate[] getAcceptedIssuers();
}

2. 非严格校验

更改AndroidManifest.xml

<application
        android:networkSecurityConfig="@xml/network_security_config"
        ...
        />
         
        

按路径添加文件/…/app/src/main/res/xml/network_security_config.xml,内容如下

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates
                overridePins="true"
                src="system" />
        </trust-anchors>
    </base-config>
    <debug-overrides>
        <trust-anchors>
            <certificates
                overridePins="true"
                src="system" />
        </trust-anchors>
    </debug-overrides>
</network-security-config>     

OkHttpClient配置

OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(HTTP_TIME_OUT / 2, TimeUnit.MILLISECONDS)
                .readTimeout(HTTP_TIME_OUT / 2, TimeUnit.MILLISECONDS);
        ...
        builder.proxy(Proxy.NO_PROXY);

参考书籍:《图解HTTP》

Search

    Table of Contents