网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
字段 | 说明 | 示例 |
---|---|---|
Allow | 服务器支持的HTTP请求方法 | Allow: GET, HEAD |
Content-Disposition | 指示回复的内容是以内联的形式还是以附件的形式下载并保存到本地;也可在multipart/form-data 类型的应答消息体中,用来给出其对应字段的相关信息 | Content-Disposition: attachment; filename=“filename.jpg” |
Content-Encoding | 告知客户端服务器对实体的主体选用的内容编码方式 | Content-Encoding: gzip |
Content-Language | 实体主体使用的自然语言 | Content-Language: zh-CN |
Content-Length | 实体部分大小 | Content-Length: 15000 |
Content-Location | 返回报文主体返回资源对应的URI | Content-Location: httpo://www.example.com/index.html |
Content-MD5 | 检查报文主体在传输过程中是否保持完整,对报文主体执行 MD5 算法获得218位二进制数,再通过 Base64 编码后将结果写入 | Content-MD5: ZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2U= |
Content-Range | 针对范围请求,表示当前发送部分及整个实体大小 | Content-Range: bytes 5001-10000/10000 |
Content-type | 实体主体内对象的媒体类型 | Content-Type: text/html; charset=utf-8 |
Expires | 将资源失效日期告知客户端 | Expires: Wed, 04 Jul 2012 08:26:05 GMT |
Last-Modified | 资源最终修改时间 | Last-Modified: wed, 25 May 2018 09:11:40 GMT |
跨域资源共享首部
跨域资源共享标准新增了一组HTTP头部字段,属于扩展首部,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,对那些可能对服务器产生副作用的HTTP请求方法,浏览器必须先使用OPTIONS方法发起一个预检请求,从而获知服务器是否允许该跨域请求。服务器允许后才发起实际的HTTP请求。预检请求的返回中,服务器也可以通知客户端是否需要携带身份凭证。
字段 | 说明 | 示例 |
---|---|---|
Access-Control-Allow-Credentials | 指示的请求的响应是否可以暴露于该页面,当true 值返回时它可以被暴露,凭证是 Cookie ,授权标头或 TLS 客户端证书 | Access-Control-Allow-Credentials: true |
Access-Control-Allow-Headers | 用于预检请求中,列出了将会在正式请求的Access-Control-Request-Headers字段中出现的首部信息 | Access-Control-Allow-Headers: X-Custom-Header |
Access-Control-Allow-Methods | 在对预检请求的应答中明确了客户端所要访问的资源允许使用的方法或方法列表 | Access-Control-Allow-Methods: POST, GET, OPTIONS |
Access-Control-Allow-Origin | 指定了该响应的资源是否被允许与给定的origin共享 | Access-Control-Allow-Origin: developer.mozilla.org |
Access-Control-Expose-Headers | 出了哪些首部可以作为响应的一部分暴露给外部 | Access-Control-Expose-Headers: Content-Length, X-Kuma-Revision |
Access-Control-Max-Age | 表示预检请求的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息)可以被缓存多久 | Access-Control-Max-Age: 600 |
Access-Control-Request-Headers | 出现于预检请求中,用于通知服务器在真正的请求中会采用哪些请求头 | Access-Control-Request-Headers: X-PINGOTHER, Content-Type |
Access-Control-Request-Method | 出现于预检请求中,用于通知服务器在真正的请求中会采用哪种HTTP方法 | Access-Control-Request-Method: POST |
安全性的不足
- 通信使用明文,内容可能会被窃听(可窃听)。
- 无法证明报文的完整性,内容有可能已遭篡改(可篡改)。
- 不验证通信方的身份,因此有可能遭遇伪装(可冒充)。
HTTPS协议
可以理解为HTTP+SSL/TLS, 即 HTTP 下加入 SSL/TLS 层,用于安全的 HTTP 数据传输,简单来说HTTP + 加密 + 认证 + 完整性保护 = HTTPS,用来解决HTTP协议安全性的不足。
SSL/TLS历史
HTTPS相比HTTP多出了SSL/TLS 层,SSL 协议原本由网景公司开发,后来被 IETF 标准化,正式名称叫做 TLS,TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3,TLS1.1和TLS1.0不支持HTTP2,目前应用最广泛的应该还是 TLS 1.2,SSL协议发展历史如下:
协议 | 发布时间 | 状态 |
---|---|---|
SSL 1.0 | 未公布 | 未公布 |
SSL 2.0 | 1995年 | 已于2011年弃用 RFC6176 |
SSL 3.0 | 1996年 | 已于2015年弃用 RFC7568 |
TLS 1.0 | 1999年 | RFC2246 |
TLS 1.1 | 2006年 | RFC4346 |
TLS 1.2 | 2008年 | RFC5246,目前最广泛应用 |
TLS 1.3 | 2018年 | RFC8446 |
TLS1.2单向认证流程
以目前使用最广泛的TLS1.2说明下认证流程即握手流程,认证分为单向和双向两种模式,单向认证客户端验证服务端证书合法即可访问,一般Web应用都是采用SSL单向认证的;双向认证需要客户端和服务器都需要持有证书,两者证书验证均合法才可以继续访问。
使用wireshark抓包TLS1.2,单向认证流程如下:
-
客户端发送Client Hello。将一个Unix时间戳、TLS版本、支持的所有加密套件、支持的签名算法、生成的随机数Random_C等发送给服务器。
-
服务器发送Server Hello。将服务器Unix时间戳、生成的随机数Random_S、协商的加密算法套件等发送给客户端。
-
服务器发送Certificate、Server Key Exchange、Server Hello Done。Certificate是数字证书,Server Key Exchange为公钥参数(有时也可不需要),Server Hello Done表明服务器已经将所有预计的握手消息发送完毕。
-
客户端发送Client Key Exchange、Change Cipher Spec、Encrypted Handshake Message。客户端首先需要校验证书,证书向上按照证书链逐级校验,每一级证书校验过程是通过拿到证书签发者(Issuer)的证书中的公钥(证书 = 使用者公钥 + 主体信息如公司名称等 + CA对信息的确认签名 + 指纹)对本级证书(Subject)的签名进行数学验证,并校验证书是否被吊销,是否在有效期,是否与域名匹配等,验证成功即证书有效,整个一级一级验证上去,形成信任链,如果校验不通过则中断连接,浏览器弹出警告,校验正确后解析得到服务器
公钥
,并发送Client Key Exchange、Change Cipher Spec、Encrypted Handshake Message。Client Key Exchange:生成一个随机数 Pre-master,并用证书公钥加密
,通过Fuc(random_C, random_S, Pre-Master)生成一个协商密钥;Change Cipher Spec:通知服务器协商完成,以后就使用上面生成的协商密钥进行对称加密;Encrypted Handshake Message:结合之前所有通信参数的 hash 值与其它相关信息生成一段数据,采用协商密钥与算法进行加密。 -
服务器发送Change Cipher Spec、Encrypted Handshake Message。服务器使用
私钥解密
得到 Pre-master数值,基于之前交换的两个明文随机数 random_C 和 random_S,同样通过Fuc(random_C, random_S, Pre-Master)得到协商密钥,计算之前所有接收信息的 hash 值,然后解密客户端发送的 encrypted_handshake_message,验证数据和密钥正确性,验证通过之后,服务器同样发送 change_cipher_spec 以告知客户端后续的通信都采用协商的密钥与算法进行加密通信;encrypted_handshake_message:服务器也结合所有当前的通信参数信息生成一段数据并采用协商密钥与算法加密并发送到客户端。 -
客户端计算所有接收信息的hash值,并采用协商密钥解密encrypted_handshake_message,验证服务器发送的数据和密钥,验证通过则握手完成。
-
开始使用协商密钥与算法进行加密通信。(Encrypted Alert是由客户端或服务器发送,意味着加密通信因为某些原因需要中断,警告对方不要再发送敏感的数据)。
Android抓包HTTPS
HTTP/HTTPS抓包工具有不少,常见的电脑端抓包工具有fiddler、Charles、Burp Suite、whistle、AnyProxy,Android端抓包APP也有HttpCanary,wireshark也可以抓包但是不可以解密HTTPS内容。
使用Charles抓包安卓HTTP很简单,将手机WIFI设置代理为Charles,步骤如下:
- 为方便设置代理,使手机与电脑处于同一局域网(连接同一个WIFI,或者电脑连到同一个路由器的LAN端口)。
- 电脑使用
ipconfig
查看局域网ip,并打开Charles。 - 手机连接的WIFI–高级设置–代理服务器,代理服务器填写电脑的局域网ip,Charles的代理端口默认8888。
- 电脑端Charles允许连接即可。
- Charles–Proxy–SSL Proxying Setting,Enable SSL Proxying打勾,add添加抓包的Host和Port,一般Port都是443,Host和Port都填*则抓包所有。
打开APP就能在Charles上看到抓包的HTTP,但是HTTPS都会显示为Unknown,因为还没有安装Charles的证书。手机设置代理以后,浏览器访问chls.pro/ssl
下载Charles证书并安装。Android 7.0以下直接安装即可,但是Android 7.0及以上默认不信任用户自己安装的证书,而只信任系统预设的证书,解决方法有:
-
手动制作Charles证书,按照Android系统预设的格式,并推到
/system/etc/security/cacerts
目录下,从而让系统把Charles证书当作系统证书 -
如果使用Magisk实现的root,Magisk安装MagiskTrustUserCerts模块,模块原理同上,可以把自定义的用户证书当作系统证书
-
反编译apk,资源文件中添加network_security_config.xml,修改AndroidManifest.xml(修改APP的网络安全配置,信任用户证书)
AndroidManifest.xml修改:
<manifest … >
…
network_security_config.xml内容:
<?xml version="1.0" encoding="utf-8"?>- frida或者xposed hook实现证书信任
在JSSE中证书信任管理器类实现了X509TrustManager接口,我们可以自己实现一个X509TrustManager,通过hook修改掉网络请求库的X509TrustManager配置。
比如自定义X509TrustManager实现信任所有服务端的证书(无论是否过期、是否经过认证):
public class TrustAllManager implements X509TrustManager {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[0];
}
}
okhttp的X509TrustManager设置为:
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(createAllSSLSocketFactory(), new TrustAllManager());
我们可以通过hook OkHttpClient.Builder类的sslSocketFactory方法,实现修改X509TrustManager配置,从而实现用户证书的信任。
HTTPS抓包原理
Charles、fiddler、HttpCanary抓包都是基于代理实现的,对HTTPS的抓包原理其实也都差不多,类似于中间人攻击,原理如下(图片来源于谈移动端抓包方式和原理及如何防犯中间人攻击):
- TLS握手时拦截服务器证书,得到服务器公钥,并将Charles自己的证书发送给客户端,客户端原本校验Charles证书不通过,但我们可以手动使客户端信任Charles证书。
- Charles拦截请求得到Random_S和Random_C等未加密信息,其他加密部分比如Pre-Master由于是使用Charles的证书公钥加密的,Charles可以使用自己的私钥解密得到内容,再使用服务器证书公钥重新加密后发送给服务器,从而与服务器完成TLS认证,并获取到密钥。
- 每次发送HTTPS请求报文经过Charles,Charles再使用得到的对称密钥进行解密。
Android防抓包策略及绕过思路
Android上HTTPS抓包成本并不算高,使系统信任第三方证书就能够实现抓包,Android 7.0 (API 24)及以上虽默认不再信任用户CA,提高了安全性但抓包成本也不算高,还可添加其他防抓包策略进一步提高安全性。
设置无代理模式
由于Charles、fiddler这些抓包工具是基于代理实现的(wireshark不是基于代理,而是网卡抓包),所以可以将APP所用的HTTP客户端设置为无代理,设置之后HTTP客户端不会连接到代理服务器,这样的话Charles就无法直接抓包了,比如OkHttp
配置无代理:
OkHttpClient.Builder()
.proxy(Proxy.NO_PROXY)
.build()
绕过方案:
- 手动修改DNS。由上面的代理原理图可知,若请求不走代理,就要通过DNS解析获取ip地址再发送请求,我们可以直接修改Android的DNS配置,将请求域名解析到Charles代理服务器上,从而实现抓包。
- VPN流量转发。使用drony之类的APP先将手机请求导到VPN,再对VPN的网络进行Charles的代理。
- 使用frida或者xposed hook
OkHttpClient.Builder
的proxy
方法,使无代理配置不起作用。
增强本地证书校验
APP本地做证书校验时不仅仅校验公钥,并且设置更为严格的校验模式,直接安装的Charles证书将因为格式问题不能验证通过,可以通过实现X509TrustManager
接口实现。这种方式其实适用于Android 7.0以下,Android 7.0以上通过MagiskTrustUserCerts等方式安装的证书由于已经被当作系统内置证书,这种方式应该不再起作用。
SSL Pinning证书锁定
应用中只信任固定证书或是公钥,将可信 CA 限制在一个很小的 CA 集范围内,应用的服务器将使用这个集合,这样可以防止因泄露系统中其他 100 多个 CA 中的某个 CA 而破坏应用安全通道。
通常有两种锁定方式证书固定和公钥固定。证书固定:将证书的某些字节码硬编码在用程序中,证书校验时检查证书中是否存在相同的字节码;公钥固定:网站会提供已授权公钥的哈希列表,指示客户端在后续通讯中只接受列表上的公钥。
OkHttp
配置实现证书锁定,对特定的host做证书公钥验证,公钥经过Sha1算法hash一下,然后Base64加密一次,然后在结果前面加上字符串"sha256/“或者"sha1/”:
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(example.com, “sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=”)
.build();
OkHttpClient client = new OkHttpClient();
client.setCertificatePinner(certificatePinner);
Android 7.0及以上实现证书锁定:
<?xml version="1.0" encoding="utf-8"?> example.com 7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y= fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=绕过方案:
- 可以尝试Xposed+JustTrustMe模块或者JustTrustMePlus模块或者TrustMeAlready模块
- Frida+DroidSSLUnpinning脚本
- hook,需要反编译分析源码
TLS双向认证
TLS认证有单向认证和双向认证模式,双向认证除了客户端去验证服务器端的证书外,服务器也同时需要验证客户端的证书,如果没有通过验证,则会拒绝连接,如果通过验证,服务器获得用户的公钥。
绕过方案:双向认证需要客户端证书,所以APP内是要有证书的并且有操作证书的地方,hook这部分代码获取证书及密钥,再将证书格式转换一下,导入到Burp Suite或者Charles这些抓包软件中,实现抓包。
此外,APP内肯定有传输内容SSL解密的实现,针对这部分代码进行hook,也可直接拿到数据,这也是更为通用的获取请求内容的办法,实现方式可以参考Frida的脚本frida_ssl_logger。
Android网络安全方面配置可以查看developer android,此外还可通过HTTPS请求的报文密、重要请求走Socket通信,APP加固防止hook等手段提高安全性。
参考
最后
在这里小编整理了一份Android大厂常见面试题,和一些Android架构视频解析,都已整理成文档,全部都已打包好了,希望能够对大家有所帮助,在面试中能顺利通过。
喜欢本文的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
的话,不妨顺手给我点个小赞、评论区留言或者转发支持一下呗
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!