HTTPS 指南
2016-12-29 08:34
260 查看
苹果在 WWDC 2016 上宣布:2016 年底将要求所有 APP 适配苹果的 App Transport Security,简单地说就是除了特殊情况(浏览器、第三方服务、媒体)外,APP 跟服务端的通信必须使用 HTTPS 协议,否则 iOS 9 和 macOS 10.11 起,操作系统将有能力阻止所有的明文 HTTP 请求。在上面的 session 中,苹果还对具体的细节做出了要求。不过,就在今天,苹果宣布将这个 deadline 无限期推迟。
本文将着重以大部分 iOS 开发者能理解的方式介绍 APP 启用 HTTPS 支持的过程中跟 APP 相关的部分,剩余的协议细节将一笔带过。
HTTPS 基础
定义
HTTPS 看似跟 HTTP 一样,其实它只是看起来跟 HTTP 一样,实际上是一种新的网络架构。在当前情况下,HTTPS 的英文全称应该是 HTTP over TLS。
HTTPS 请求 和 HTTP 请求的异同
普通 HTTP 请求直接基于 TCP,在互联网上明文传播,而且没有任何校验,链路上的每一个节点都可以对数据包进行篡改,使用手机网络访问 HTTP 网站被插入流量球甚至广告等运营商劫持行为就是最常见的例子。而 HTTPS 请求运行在 TLS 层之上,TLS 运行在 TCP 上,TLS 有独特的握手、建立连接、数据验证机制,让运行商劫持无处下手:只要任何一个数据包被篡改,数据校验就会失败,这个请求会客户端直接抛弃,网页不会显示。当我们用 HTTP 协议来解释 TLS 层携带的内容时,这个东西就被称为 HTTPS 啦。
HTTP 协议简析
HTTP 协议是一个非常简单而强壮的协议,它规定了以文本方式解析数据后哪一部分该代表什么:头部携带特定信息,正文部分被渲染为网页。所以,任何数据都可以被 HTTP 协议解析,无论他是基于 TCP 还是 TLS 传输,或者只是硬盘上的一个文件。
请注意,此处的 HTTP 协议和上一小节中的 HTTP 请求是两个概念。
HTTPS 证书
证书是什么
下面两张图分别是我的个人博客的一张旧证书的 cer 和 crt 两种格式,在 Finder 中点击空格预览的结果:
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083021742-966437612.jpg)
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083051023-1320817233.jpg)
下面是 crt 格式的证书内容:
—–BEGIN CERTIFICATE—–
MIIErzCCA5egAwIBAgIQYrp2Mj1s3GeeVubYEGEguTANBgkqhkiG9w0BAQsFADBV
... 省略N个字...
XKK1E/BNFR7GR8i1NfL15KdIGUmsklT60vooRd7zM9ai8vtmkg9xykwpgUPbTjcd
mRAb
—–END CERTIFICATE—–
证书就是使用特殊格式加密的一段字符串,可以被读取并拿出关键信息。iOS 中 NSURLSession 验证的就是 cer 格式的这个证书。
证书周边知识
下面是一个 HTTPS 证书典型的购买、部署流程:
在 *UNIX 环境下使用 openssl 工具生成一对一匹配的 私钥 和 CERTIFICATE REQUEST 文件(以 —–BEGIN CERTIFICATE REQUEST—– 开头)。私钥为绝密,绝对不能泄露,最好在生产服务器直接生成,这样就不需要网络传输,更加安全。
将 CERTIFICATE REQUEST 文件提交到证书服务机构,服务机构根据证书级别进行 域名认证、公司认证、安全认证 等不同级别的安全验证。
验证通过后,服务机构将基于我们提交的 CERTIFICATE REQUEST 内容,使用他已有的证书派生出子证书,提供给我们下载。
我们拿到服务机构颁发的两个 crt 格式的证书(root 证书 及我们的域名证书),再配合本地的私钥,到 Apache、Nginx 等 web server 上部署,部署时会验证“私钥”是否和“域名证书”匹配。
用户在以 HTTPS 协议访问网站时,浏览器会进行如下几步安全验证:
域名证书中的域名和实际域名是否一致
域名证书和 root 证书是否匹配
root 证书是否可信
需要注意的点
在某些低安全级别证书申请中(如仅验证域名所有权的证书),私钥可以让签发服务器代为生成,但这样做有一定的安全风险。
root 证书也可以在我们本机生成,如 12306 的自签名证书,但这样不会被普通浏览器信任。
私钥为绝密,因为证书全部都是公开的,任何人都可以提取,如果私钥被别人获取,被部署到别人的服务器中,那所有人就会认为那台服务器是完全合法的。这已经不是中间人攻击了,这时候他就是你。
NSURLSession 对证书的验证
证书验证方法
在 URLSessionDelegate 中有一个方法 func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 专门用来处理 HTTPS 证书的处理。具体操作可以参考 Pitaya 中的代码。
我简单复述一下处理流程:
发现是 HTTPS 请求,取出证书
进入证书处理流程:
本地证书分两种情况:① 本地存储着证书服务机构颁发的 crt 文件转换而来的 cer 文件,使用 NSData 进行内容对比 ② 本地存储着自签名的 cer 格式的证书,使用 NSData 进行内容对比
匹配成功,手动让请求继续,这一步可以让自签名证书绕过 iOS 系统的证书合法性验证
匹配失败,进入错误处理流程
如果自签名证书不做手动处理,那么在这个方法结束后链接就会被系统关闭,因为 root 证书不合法。
所以,在 APP 提交的时候,苹果会检查是否将 ATS 配置为了“全部无脑通过”,这种操作是被禁止的。当然,苹果也可以在系统层面一刀切,但是那样得挂多少个 APP 啊,苹果不会那么做。所以会被影响的应该只是新提交的 ipa。
SSL 钢钉原理
如果有人通过一些手段通过了域名所有权认证(非常容易,域名邮箱、DNS指向甚至在根目录放一个文件都可以验证通过),拿到了一个合法的对应你的域名的 HTTPS 证书,这时候他在广场开放了一个没有密码的 wifi,命名为 CMCC,这样,几乎所有的开着 wifi 的手机都会自动连接,这时候他只需要做一个简单的 DNS 劫持,就可以把所有应该向你网站发送的需求劫持到他那里。
但是:如果你做了 SSL 钢钉验证,那么他的证书就不会被验证通过。他也没法用你的证书启动服务,因为私钥和证书的验证是 SSL 协议强制做的,他没有你的私钥,他的 web server 就没法启动。所以,私钥千万不能泄露。
关于自签名证书
自签名证书不会被浏览器信任,因为每次有新的 HTTPS 证书到达某个操作系统时,系统会去访问 root 证书的服务器以确定域名证书的身份,这些合法的 root 证书服务商就是固定的那几家,显然自签名证书不会被信任,所以我们在 12306 抢票的时候需要先下载他的自签名证书的 root 证书并手动信任,不然就打不开页面。
我们可以看到,自签名证书的验证在代码层面,在审核的时候是完全不可感知的,所以就没有什么“苹果不接受自签名证书”之类的问题了。而且,自签名证书被广泛的用于各种系统内部的连接加密,不是苹果可以一刀切的:如果粗暴的在操作系统层面阻止了自签名证书,导致企业客户的系统突然挂掉,后果不可想象。
关于证书更换
证书都有有效期,在过期之前需要申请新证书,这时候 SSL 钢钉该怎么处理呢?动态下发当然是不行的,为什么要验证证书?就是因为网络不可信任。Pitaya 前两天加入了这个逻辑:在新证书和旧证书交接的一段时间内,上线新版本,同时包含新旧证书,这样可以保证更新过的用户可以对证书更换无感。
另外,设置 SSL 钢钉不适合面向普通用户的 APP,因为总是有人万年不更新,这更适合企业内部 APP,可以通过行政手段及自驱力(业绩啊,提成啊)推动更新。
关于所谓的双向验证
感觉这里是大家误解最大的地方:大部分人所谓的“双向验证”就是自签名证书的验证并手动继续而已。
HTTPS 是支持双向认证的,不过那指的是客户端(浏览器或 APP)也像服务端一样,在发送请求给服务端的时候带上证书,再由服务端使用对应的私钥进行验证。一般 APP 不需要这么做。
苹果的要求
后端
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083215820-38573928.jpg)
前两条拿给后端看就行,第三条用 Nginx 也很容易实现。最后一条 Exception 就是苹果马上就不支持的。
[b]iOS 端[/b]
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083305601-1698460115.jpg)
使用这些 API 可以单独绕过。
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083333601-1098243297.jpg)
流媒体文件可以添加例外,WKWebView 可以直接设置为绕过。
总结
购买的证书:什么都不用管,改一下服务器地址就行,代码完全不用改。
自签名证书:找后端哥们儿要一个 crt,自己提取也行,转换成 cer ,放到相应的方法里,手动让处理流程继续即可。
本文将着重以大部分 iOS 开发者能理解的方式介绍 APP 启用 HTTPS 支持的过程中跟 APP 相关的部分,剩余的协议细节将一笔带过。
HTTPS 基础
定义
HTTPS 看似跟 HTTP 一样,其实它只是看起来跟 HTTP 一样,实际上是一种新的网络架构。在当前情况下,HTTPS 的英文全称应该是 HTTP over TLS。
HTTPS 请求 和 HTTP 请求的异同
普通 HTTP 请求直接基于 TCP,在互联网上明文传播,而且没有任何校验,链路上的每一个节点都可以对数据包进行篡改,使用手机网络访问 HTTP 网站被插入流量球甚至广告等运营商劫持行为就是最常见的例子。而 HTTPS 请求运行在 TLS 层之上,TLS 运行在 TCP 上,TLS 有独特的握手、建立连接、数据验证机制,让运行商劫持无处下手:只要任何一个数据包被篡改,数据校验就会失败,这个请求会客户端直接抛弃,网页不会显示。当我们用 HTTP 协议来解释 TLS 层携带的内容时,这个东西就被称为 HTTPS 啦。
HTTP 协议简析
HTTP 协议是一个非常简单而强壮的协议,它规定了以文本方式解析数据后哪一部分该代表什么:头部携带特定信息,正文部分被渲染为网页。所以,任何数据都可以被 HTTP 协议解析,无论他是基于 TCP 还是 TLS 传输,或者只是硬盘上的一个文件。
请注意,此处的 HTTP 协议和上一小节中的 HTTP 请求是两个概念。
HTTPS 证书
证书是什么
下面两张图分别是我的个人博客的一张旧证书的 cer 和 crt 两种格式,在 Finder 中点击空格预览的结果:
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083021742-966437612.jpg)
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083051023-1320817233.jpg)
下面是 crt 格式的证书内容:
—–BEGIN CERTIFICATE—–
MIIErzCCA5egAwIBAgIQYrp2Mj1s3GeeVubYEGEguTANBgkqhkiG9w0BAQsFADBV
... 省略N个字...
XKK1E/BNFR7GR8i1NfL15KdIGUmsklT60vooRd7zM9ai8vtmkg9xykwpgUPbTjcd
mRAb
—–END CERTIFICATE—–
证书就是使用特殊格式加密的一段字符串,可以被读取并拿出关键信息。iOS 中 NSURLSession 验证的就是 cer 格式的这个证书。
证书周边知识
下面是一个 HTTPS 证书典型的购买、部署流程:
在 *UNIX 环境下使用 openssl 工具生成一对一匹配的 私钥 和 CERTIFICATE REQUEST 文件(以 —–BEGIN CERTIFICATE REQUEST—– 开头)。私钥为绝密,绝对不能泄露,最好在生产服务器直接生成,这样就不需要网络传输,更加安全。
将 CERTIFICATE REQUEST 文件提交到证书服务机构,服务机构根据证书级别进行 域名认证、公司认证、安全认证 等不同级别的安全验证。
验证通过后,服务机构将基于我们提交的 CERTIFICATE REQUEST 内容,使用他已有的证书派生出子证书,提供给我们下载。
我们拿到服务机构颁发的两个 crt 格式的证书(root 证书 及我们的域名证书),再配合本地的私钥,到 Apache、Nginx 等 web server 上部署,部署时会验证“私钥”是否和“域名证书”匹配。
用户在以 HTTPS 协议访问网站时,浏览器会进行如下几步安全验证:
域名证书中的域名和实际域名是否一致
域名证书和 root 证书是否匹配
root 证书是否可信
需要注意的点
在某些低安全级别证书申请中(如仅验证域名所有权的证书),私钥可以让签发服务器代为生成,但这样做有一定的安全风险。
root 证书也可以在我们本机生成,如 12306 的自签名证书,但这样不会被普通浏览器信任。
私钥为绝密,因为证书全部都是公开的,任何人都可以提取,如果私钥被别人获取,被部署到别人的服务器中,那所有人就会认为那台服务器是完全合法的。这已经不是中间人攻击了,这时候他就是你。
NSURLSession 对证书的验证
证书验证方法
在 URLSessionDelegate 中有一个方法 func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) 专门用来处理 HTTPS 证书的处理。具体操作可以参考 Pitaya 中的代码。
我简单复述一下处理流程:
发现是 HTTPS 请求,取出证书
进入证书处理流程:
本地证书分两种情况:① 本地存储着证书服务机构颁发的 crt 文件转换而来的 cer 文件,使用 NSData 进行内容对比 ② 本地存储着自签名的 cer 格式的证书,使用 NSData 进行内容对比
匹配成功,手动让请求继续,这一步可以让自签名证书绕过 iOS 系统的证书合法性验证
匹配失败,进入错误处理流程
如果自签名证书不做手动处理,那么在这个方法结束后链接就会被系统关闭,因为 root 证书不合法。
所以,在 APP 提交的时候,苹果会检查是否将 ATS 配置为了“全部无脑通过”,这种操作是被禁止的。当然,苹果也可以在系统层面一刀切,但是那样得挂多少个 APP 啊,苹果不会那么做。所以会被影响的应该只是新提交的 ipa。
SSL 钢钉原理
如果有人通过一些手段通过了域名所有权认证(非常容易,域名邮箱、DNS指向甚至在根目录放一个文件都可以验证通过),拿到了一个合法的对应你的域名的 HTTPS 证书,这时候他在广场开放了一个没有密码的 wifi,命名为 CMCC,这样,几乎所有的开着 wifi 的手机都会自动连接,这时候他只需要做一个简单的 DNS 劫持,就可以把所有应该向你网站发送的需求劫持到他那里。
但是:如果你做了 SSL 钢钉验证,那么他的证书就不会被验证通过。他也没法用你的证书启动服务,因为私钥和证书的验证是 SSL 协议强制做的,他没有你的私钥,他的 web server 就没法启动。所以,私钥千万不能泄露。
关于自签名证书
自签名证书不会被浏览器信任,因为每次有新的 HTTPS 证书到达某个操作系统时,系统会去访问 root 证书的服务器以确定域名证书的身份,这些合法的 root 证书服务商就是固定的那几家,显然自签名证书不会被信任,所以我们在 12306 抢票的时候需要先下载他的自签名证书的 root 证书并手动信任,不然就打不开页面。
我们可以看到,自签名证书的验证在代码层面,在审核的时候是完全不可感知的,所以就没有什么“苹果不接受自签名证书”之类的问题了。而且,自签名证书被广泛的用于各种系统内部的连接加密,不是苹果可以一刀切的:如果粗暴的在操作系统层面阻止了自签名证书,导致企业客户的系统突然挂掉,后果不可想象。
关于证书更换
证书都有有效期,在过期之前需要申请新证书,这时候 SSL 钢钉该怎么处理呢?动态下发当然是不行的,为什么要验证证书?就是因为网络不可信任。Pitaya 前两天加入了这个逻辑:在新证书和旧证书交接的一段时间内,上线新版本,同时包含新旧证书,这样可以保证更新过的用户可以对证书更换无感。
另外,设置 SSL 钢钉不适合面向普通用户的 APP,因为总是有人万年不更新,这更适合企业内部 APP,可以通过行政手段及自驱力(业绩啊,提成啊)推动更新。
关于所谓的双向验证
感觉这里是大家误解最大的地方:大部分人所谓的“双向验证”就是自签名证书的验证并手动继续而已。
HTTPS 是支持双向认证的,不过那指的是客户端(浏览器或 APP)也像服务端一样,在发送请求给服务端的时候带上证书,再由服务端使用对应的私钥进行验证。一般 APP 不需要这么做。
苹果的要求
后端
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083215820-38573928.jpg)
前两条拿给后端看就行,第三条用 Nginx 也很容易实现。最后一条 Exception 就是苹果马上就不支持的。
[b]iOS 端[/b]
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083305601-1698460115.jpg)
使用这些 API 可以单独绕过。
![](https://images2015.cnblogs.com/blog/708266/201612/708266-20161229083333601-1098243297.jpg)
流媒体文件可以添加例外,WKWebView 可以直接设置为绕过。
总结
购买的证书:什么都不用管,改一下服务器地址就行,代码完全不用改。
自签名证书:找后端哥们儿要一个 crt,自己提取也行,转换成 cer ,放到相应的方法里,手动让处理流程继续即可。
相关文章推荐
- 大型网络架构模式:电商
- 网络编程(48)—— windows平台下的四种线程同步技术(二)
- 费用流 【bzoj1283】序列
- Android okHttp 实战(五):okHttp网络请求之Retrofit+Okhttp+RxJava组合
- Android okHttp 实战(四):okHttp网络请求之缓存控制Cache-Control
- http协议的四次握手
- Android okHttp 实战(三):okHttp网络请求之Json解析
- Android okHttp 实战(二):okHttp网络请求之文件上传下载
- Android okHttp 实战(一):okHttp网络请求之Get/Post请求
- Android OkHttp官方教程解析-彻底入门OkHttp使用
- Unix网络编程 刨坑大法 凌晨两点写博客 也是没谁了
- C#-WebForm-WebForm开发基础、如何给控件注册事件?——事件委托写法、http无状态性、三层结构
- let's encrypt ssl https证书申请 DNS TXT记录 转换成tomcat的keystore.md
- HTTP 状态码详解
- java模拟http请求的代码实现
- HttpHelper类使用方法
- QQ--基于TCP/UDP协议的通讯原理
- HttpHelper类使用方法
- QQ--基于TCP/UDP协议的通讯原理
- c# 【MVC】WebApi通过HttpClient来调用Web Api接口