赞
踩
Http协议由于是明文传送,所以存在三大风险:
1、被窃听的风险:第三方可以截获并查看你的内容
2、被篡改的危险:第三方可以截获并修改你的内容
3、被冒充的风险:第三方可以伪装成通信方与你通信
正式因为存在以上三大安全风险,所以才有了Https的出现。而且从Android 9.0开始使用http进行网络请求会报错。Https涉及到了很多概念,比如SSL/TLS、数字证书、数字签名、加密、认证、公钥和私钥等都是非常重要的技术点。
对称加密,对于加密和解密算法来说,加密和解密的密钥是相同的,如同同一把锁,加密密钥和解密密钥都是相同的一把钥匙。用密钥加密后,得到加密后的密文,随后可以直接用其进行解密,得到初始的原文。
对于通信的双方,通过对称加密,可以很方便的将通信内容进行加密,然后进行安全传输,能够有效的防止信息在中途被中间人获取到原文。即使信息中途有被截获的可能,但只要密钥没有泄露,信息本身还是安全的。
鉴于安全、方便、高效等特点,对称加密被广泛的使用在信息传输过程中。
对称加密存在最大的一个问题,在于如何将对称密钥安全的告知对方。在一个客户端可以同时和多个服务端通信,一个服务端可以同时和多个客户端通信的现实环境下,这种关系是多对多的,而对称密钥又只能由当前通信的客户端和服务端所持有,因此,在实际通信过程中,对称密钥的协商成为现实中难点。
与对称加密不同的是,非对称加密同时具有公钥和私钥。如果用公钥进行加密,则只能通过对应的私钥去解密,如果用私钥进行加密,则只能通过对应的公钥去解密。在非对称加密体系中,公钥和私钥是一对活宝,缺少任何一方都是不可以的。私钥往往都主体对象自己保留,公钥则可以直接向外界公开。
公私钥的存在,使得非对称加密具有了相对对称加密明显的优点。
1、公钥直接向外界公开,外界任一主体通过公钥进行加密,可以直接向持有私钥的主体安全的发送加密后的密文,私钥主体拿到密文后,通过自身持有的私钥对应解密,获取明文,以此完成信息的安全传输过程,且无需面临对称加密中对称密钥的安全协商问题。
2、私钥主体通过私钥对信息进行加密,外界任一主体通过公钥去进行解密,如果可以成功解密,则能反向确定对应通信对方的身份,相当于完成了身份校验。以此,非对称加密具有很好的身份验证能力。
凡事有利有弊,非对称加密相比对称加密,因为本身功能更加强大,在算法设计上对对应的也更加复杂,在加解密效率上,远低于对称加密。
散列算法,也称之为摘要算法或哈希算法,可以将任一数据对象压缩成数据摘要,对于同一散列算法,压缩后的数据摘要具有特定的长度和格式,以此形成数据的"指纹"。原始数据对象的任一细小的改动,都可能使得新的"数字指纹"有着很大不同。
往往通过散列算法,可以判断两个数据对象是否相同,因为相同的数据对象,通过散列算法形成的"数字指纹"必定是相同的,但是相同的"数字指纹",对应的原始数据对象不一定是相同,因为可能存在散列冲突问题。
散列算法,在现实中就有广泛的使用场景,例如最常用的对两个文件对象进行MD5,判断其内容是否相同。
严格意义上来说,散列算法与加密算法(对称加密或非对称加密)是有着本质区别的,加密算法,对应的是可以解密的,目的是进行数据加密后的安全存储或传输,是可以通过密钥得到原文的,是可逆的过程。而散列算法,本质上"数字指纹"的范畴,通过散列算法形成的"数字签名",直接在算法层面是不能得到原文的,是不可逆的。当然,通过彩虹表和数据字典这种形式的所谓解密,本质上只是暴力破解的过程。
Https = Http + SSL/TLS = Http + 内容加密 + 身份验证 + 数据完整性保护。
Https通信过程包含了密钥协商和加密通信两个阶段。
密钥协商是整个Https通信过程中的重点部分。此处的密钥,指的是客户端与服务端协商后续用于信息加密的对称密钥。协商的主要过程包括:
1、客户端向服务端发送ClientHello消息,其中包含客户端随机生成的随机数RNc、客户端的协议版本信息、加密套件信息等。
2、服务端接收消息,并向客户端发送ServerHello消息,其中包括服务端随机生成的随机数RNs、服务端协议版本信息、加密套件等信息。
3、服务端向客户端发送包含了服务端公钥的证书。
4、服务端向客户端请求包含了客户端公钥的证书。
5、客户端核验服务端证书,并向服务端发送包含了客户端公钥的客户端证书。
6、服务端核验客户端证书。
7、客户端生成随机数PMS,并通过服务端公钥叫PMS进行加密,发送给服务端。
8、客户端和服务端通过同样的算法,以及RNc、RNs、PMS,分别生成对称加密密钥,因为算法和参数都是相同的,因此,两端生成的对称密钥也是相同的。
以此完成两端对称密钥的协商过程。
密钥协商完成后,SSL握手过程结束。后续的通信过程都基于协商了的对称密钥进行加密,再进行传输。这个过程与一般意义上的加密通信过程没有差别。
密钥协商的最终目的,是在客户端和服务端安全的同时生成用于后续加密通信过程的对称密钥。这也正是Https所谓的安全的根本。
密钥协商过程中,客户端和服务端如何安全的交换彼此的非对称加密的公钥成为重点。因为一旦安全的获知对方的公钥后,就可以通过对方公钥进行加密,进一步完成后续对称密钥的协商。在获得对方公钥时,如何对通信对方身份进行核验,以确保此公钥就是真实的通信对方的公钥?
公钥不可能直接通过网络进行传输,否则依然存在信息被中间窃取和篡改的可能,进一步可能引发中间人攻击。因此,在获得对方公钥的同时,还能有一套机制去核验对方的身份,以及对方公钥的准确性,是一大难点。
直接通过网络方式,将永远是一个鸡生蛋和蛋生鸡的问题,安全性永远没法得到保障。由此,通过基于CA的方式,颁发数字证书,来对通信方的身份进行"担保",对数据完整性提供核验,被设计出来。
通过信任CA,由此信任CA颁发的数字证书,
由此信任通信方并获得对方的公钥。
对应的,现在存在两个问题:
1、CA本身的身份,如何信任?
2、信任CA后,如何校验数字证书以确认通信对方的身份并获取到对应的公钥?
实际应用中,对CA身份的信任,提供了两种途径。一种是直接基于系统的集成,将全球主要的CA本身对自己签发的数字证书集成到系统中。如Mac电脑系统或Android手机等。另一种是基于人为的手动授信,如通过浏览器下载第三方的CA数字证书,并设置对其进行信任。无论是哪种方式,系统将会对集成了的CA数字证书信任,并能获取到对应的CA公钥。
接下来以客户端校验服务端证书为例,阐述完整的身份核验和公钥获取过程。
管理员向CA申请数字证书,其中包括服务端公钥、域名、证书有效期、组织机构、地域、证书序列号、颁发机构等信息。这些信息正文,通过散列算法H,形成信息摘要,CA对生成的信息摘要,通过CA自身的非对称加密的私钥进行加密,得到密文,此密文称之为数字签名(CA对信息摘要进行了签名),信息正文、数字签名,放在一起,形成数字证书。
数字证书上,包括两大内容:
1、信息正文
2、数字签名。
服务端将证书发送给客户端后,客户端首先拿到证书的CA信息,与系统中已经授权的CA进行比较,如果发现存在同样的CA,则使用CA的公钥对其进行加密。否则将尝试基于证书链的形式对服务端证书CA的身份进行校验,通过后再基于CA公钥进行解密。否则,服务端证书校验失败。
CA公钥解密数字证书上的数字签名后,可以得到消息摘要,再通过对信息正文进行同样的散列算法,也得到消息摘要,将两个消息摘要进行对比,以完成数据完整性的校验。同时,服务端身份以及数字证书上的服务端公钥也得以确认。
Https抓包,本质上是基于中间人攻击。安装好抓包软件,开启抓包服务,将客户端(手机或浏览器等)配置好相应的代理服务器地址及端口。此时,客户端的Http/Https请求将通过抓包服务中转,对Http/Https通信服务端而言,抓包服务充当了客户端的角色,对源客户端而言,抓包服务充当了服务端角色。因此,抓包服务形成事实上的代理,且同时具有正向代理和反向代理的职能。
通信经过抓包服务,但对于Https请求,由于报文主体是加密的,此时虽然能抓到包,但看到报文主题是乱码形式。因为此时抓包服务无法对报文主体进行透明解密,也无法达到可以篡改的效果。此时,针对报文主体,更多的扮演的是一个中间代理转发通信的角色。因为此时,客户端对通信服务端的身份依然既有有效的核验过程。
在使用抓包软件时,都会要求客户端安装一个证书并设置信任。这个证书,就是抓包服务所对应的证书,客户端信任此证书后,相当于完全信任了抓包服务。此时,抓包服务具备了完整意义上的通信中转、分发、信息透明和篡改等功能,达到了完整意义上中间人攻击的效果。源客户端和源服务端实际上不再是和对方通信,只是它们以为,通信方还是彼此。
信任了抓包服务的证书,此后,源客户端实际上,是在和抓包服务进行通信,对服务端的身份和信息完整性校验,也是基于抓包服务的公钥。源客户端的请求,对抓包服务将是完全透明的,抓包服务可以篡改此请求,并扮演成源客户端角色,和源服务端通信,因为抓包服务具有源服务端的公钥和协商后的对称加密密钥,因此,服务端返回的结果,对抓包服务是透明的,抓包服务可以对其进行篡改,并返回给源客户端。这也是很多抓包工具,不但具有通信内容的透明显示,也还具有额外的调试功能,如通过对请求参数,或返回结果等的修改,达到想要的调试目的等。
主流的Http/Https抓包工具有Fiddler、Charles等。
Android开发中,通过抓包分析竞品App的通信信息,是很常见的。但往往也发现,有些App,是抓不了包的。为了自身App的安全性等考虑,正式对外发布的安装包,有必须要做防抓包处理。
抓包的本质,是中间人攻击。针对中间人攻击的原理,在其中关键环节,打破抓包工具成为完整意义上中间人的条件,便可以实现Android App的防抓包防护。
1、源客户端忽略代理设置
Android项目中,可以通过判断当前是否在使用代理,通过忽略掉代理设置,实现请求的直接连接。
一般的对是否使用代理的判断写法如下:
- private boolean isUsingProxy() {
- // 是否大于等于14
- final boolean isIceOrUpper = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
- String proxyAddress;
- int proxyPort;
-
- if (isIceOrUpper) {
- proxyAddress = System.getProperty("http.proxyHost");
- String portStr = System.getProperty("http.proxyPort");
- proxyPort = Integer.parseInt((portStr != null ? portStr : "-1"));
- } else {
- proxyAddress = Proxy.getHost(getApplicationContext());
- proxyPort = Proxy.getPort(getApplicationContext());
- }
-
- return (!TextUtils.isEmpty(proxyAddress)) && (proxyPort != -1);
- }

OkHttp直接提供了忽略代理设置的接口,如果发现正在使用代理,可以设置忽略代理。
- OkHttpClient.Builder builder = new OkHttpClient.Builder();
- ...
- builder.proxy(Proxy.NO_PROXY);
- ....
2、源客户端设备提高证书信任级别
Android 7.0开始,将网络安全配置中的证书由原来的"系统级证书"和"用户级证书"改成了"系统级证书"。这也就意味着,"用户级证书"默认将不被信任,即使用户自己,已经设置了对其信任。因此,我们会发现,默认情况下,在Android 7.0及以上手机上,是抓不了包的。
抓包代理显示的信息为:
源客户端显示的信息为:
- Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
- at com.android.org.conscrypt.TrustManagerImpl.verifyChain(TrustManagerImpl.java:661)
- at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:539)
- at com.android.org.conscrypt.TrustManagerImpl.checkTrustedRecursive(TrustManagerImpl.java:605)
- at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:495)
- at com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:418)
- at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:339)
- at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)
- at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88)
- at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:197)
- at com.android.org.conscrypt.ConscryptFileDescriptorSocket.verifyCertificateChain(ConscryptFileDescriptorSocket.java:399)
- at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
- at com.android.org.conscrypt.SslWrapper.doHandshake(SslWrapper.java:374)
- at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:217)
- ....
同时,Android对开发者提供了网络安全配置入口,如果需要抓包,只需要将"用户级证书"加入到对应配置项即可。
- <?xml version="1.0" encoding="utf-8"?>
- <network-security-config>
- <base-config>
- <trust-anchors>
- //user表示“用户级证书”
- <certificates src="user" />
- <certificates src="system" />
- </trust-anchors>
- </base-config>
- </network-security-config>
开发过程中,或内部的测试包,可能需要抓包,因为,对内环境开启此配置项是很有必须的。但对外的正式包中,应该保持系统原本配置,以提升抓包防护的安全级别。
针对不同场景下的网络安全配置需求,官方文档也给出了解决方法,例如提供了android:debuggable
为为true
情况下的专门配置。但实际App开发中,往往此开关都直接是false
配置的,解决方法也很简单,可以针对App的buildTypes
、productFlavors
甚至变体去单独配置network-security-config
即可。如针对对内的dev
环境下配置上<certificates src="user" />
,以使得可以抓包,对外环境下去掉此配置。
3、源客户端加强对服务端的信任校验。
源客户端加强对服务端的信任校验,实际中根据具体的需求场景,服务端证书的配置方式等,可以有多种方式。例如源客户端内置上服务端证书,或者对服务域名进行安全校验,又或者对证书名称进一步校验等。一旦发现不符合预期的证书信息项,则终止对应请求。
下面简单演示对服务端信息判断,一旦是抓包服务信息,则终止掉请求。
- public class ServerCertificateSecurityVerifier implements HostnameVerifier {
- ....
-
- private static final String[] DEFAULT_INSECURITY_CA_ISSUER_KEYWORDS = new String[]{
- // fiddler
- "fiddler.com",
- // fiddler
- "fiddler2.com",
- // charles
- "charlesproxy.com",
- // whistle
- "wproxy.org",
- // Android Packet Capture
- "Packet Capture CA Certificate",
- // mitmproxy
- "mitmproxy",
- // debug proxy
- "Debug Proxy"
- };
-
- @Override
- public boolean verify(String hostname, SSLSession session) {
- // 不允许抓包条件下,判断正式信息是否包含主流的抓包代理,如果,校验失败,否则,走默认流程
- try {
- for (X509Certificate x509Certificate : session.getPeerCertificateChain()) {
-
- if (x509Certificate.getIssuerDN() == null || StringUtil.isEmpty(x509Certificate.getIssuerDN().getName())) {
- continue;
- }
-
- String caIssuerCompleteName = x509Certificate.getIssuerDN().getName();
- for (String insecurityCAIssuerKey : DEFAULT_INSECURITY_CA_ISSUER_KEYWORDS) {
- // 证书信息中有抓包工具的证书信息
- if (Pattern.compile(Pattern.quote(insecurityCAIssuerKey), Pattern.CASE_INSENSITIVE)
- .matcher(caIssuerCompleteName).find()) {
- Log.w(, TAG, "ServerCertificate error... - " + caIssuerCompleteName);
- return false;
- }
- }
-
- }
-
- } catch (Exception e) {
- Log.e(TAG, e);
- }
- return verifyByOkHttpDefault(hostname, session);
- }

参考网址:
tools.ietf.org/html/rfc524…
developer.android.com/training/ar…
codelabs.developers.google.com/codelabs/an… mp.weixin.qq.com/s/3M0CqFQP2…
zh.wikipedia.org/wiki/傳輸層安全性…
zh.wikipedia.org/wiki/超文本传输安…
作者:HappyCorn
链接:https://juejin.im/post/5d3528ab6fb9a07ecf7261c2
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。