iOS Xcode ATS简介

3,238 阅读5分钟

之前项目中使用某第三方的SDK的时候出现过如下日志内容

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via > your app's Info.plist file. Cannot start load of Task <0CF595B5-17EA-44B3-8E5D-8AA70B6D28EE>.<0> since it does not conform to ATS policy NSURLConnection finished with error - code -1022 Task .<2> finished with error - code: -1022

以上输出内容是因为在iOS9的时候,苹果提供了ATS(App Transport Security),为了希望app支持HTTPS,对于HTTP请求会失败。笔者以前都是简单设置一下Info.plist文件中Allow Arbitrary Loads为YES来处理。简单粗暴地处理方式如下:

设置Allow Arbitrary Loads为YES

Apple添加ATS的用于在于保护用户信息的安全完整。先看看HTTP现存的问题,及使用HTTPS的好处。

HTTPS相对于HTTP的优势:

  • HTTP存在的问题:
      1. 通信使用明文传输,内容可能被窃听
      1. 不验证通信方身份,可能遭到中间人攻击
      1. 无法验证通信报文的完整性,因此报文传输过程中可能遭到篡改。

针对上述问题HTTPS应运而生(HTTP Over TLS 或者说SSL(TLS的前身)) :

TLS (Transport Layer Security ) SSL(Secure Sockets Layer ) 大家可能对TLS和SSL不大熟悉,但是可能用过OpenSSL。

OpenSSL 是一种功能强大的商用级全功能工具包,适用于TLS和SSL协议。可以说OpenSSL是一种TLS的实现

  • 那么HTTPS是如何去除了HTTP现存的缺点的呢:

    1. 对于HTTP明文传输部分缺点,HTTPS并不是通过加密传输内容来确保的传输安全性,而是通过TLS加密通信线路,来达到的安全传输的目的;
    1. 验证通信方身份部分,HTTPS使用的是TLS提供的验证证书的方式来确定的通信方身份;
    1. 验证通信报文部分,HTTPS通过使用TLS提供的认证和加密处理及摘要功能来确定数据通信报文的完整性。

下面我们继续看ATS相关内容。

ATS简介

ATS(App Transport Security),ATS是一种要求app或者支持HTTPS,或者在Info.plist文件中声明安全限制的技术手段。 ATS可配置项有:

    NSAllowsArbitraryLoads : Boolean
    NSAllowsArbitraryLoadsForMedia : Boolean
    NSAllowsArbitraryLoadsInWebContent : Boolean
    NSAllowsLocalNetworking : Boolean
    NSExceptionDomains : Dictionary {
        <domain-name-string> : Dictionary {
            NSIncludesSubdomains : Boolean
            NSExceptionAllowsInsecureHTTPLoads : Boolean
            NSExceptionMinimumTLSVersion : String
            NSExceptionRequiresForwardSecrecy : Boolean   // Default value is YES
            NSRequiresCertificateTransparency : Boolean
        }
    }
}

其中我们最熟悉的是设置NSAllowsArbitraryLoads为YES,之后我们就可以加载HTTP请求了。

NSAppTransportSecurity : Dictionary {
    NSAllowsArbitraryLoads : Boolean
    NSAllowsArbitraryLoadsForMedia : Boolean
    NSAllowsArbitraryLoadsInWebContent : Boolean
    NSAllowsLocalNetworking : Boolean
    NSExceptionDomains : Dictionary {
        <domain-name-string> : Dictionary {
            NSIncludesSubdomains : Boolean
            NSExceptionAllowsInsecureHTTPLoads : Boolean
            NSExceptionMinimumTLSVersion : String
            NSExceptionRequiresForwardSecrecy : Boolean   // Default value is YES
            NSRequiresCertificateTransparency : Boolean
        }
    }
}

NSAllowsArbitraryLoadsForMedia、NSAllowsArbitraryLoadsInWebContent、NSAllowsLocalNetworking 都是在iOS10及之后支持

  • NSAllowsArbitraryLoads默认为NO,如果设置为YES,是使app支持HTTP请求的一种便捷方法,但是如果自己的app的请求是支持HTTPS的,那么感觉改这个值有些过了。
  • NSAllowsArbitraryLoadsForMedia默认为NO,在使用AV Foundation framework的时候,为了一些HTTP的音视频处理改变这个值为YES。
  • NSAllowsArbitraryLoadsInWebContent默认为NO,在使用UIWebView或WKWebView加载一些HTTP请求的时候可以设置这个值为YES。
  • NSAllowsLocalNetworking默认为NO,在使用内网的时候可以设置这个值为YES。
  • NSExceptionDomains是一个字典类型可用于设置多个值。
  • domain-name-string 用于指定域名。
  • NSIncludesSubdomains 默认为NO,用于指定是否包含子域名。
  • NSExceptionAllowsInsecureHTTPLoads 默认为NO,用于指定是否允许不安全的HTTP加载。
  • NSExceptionMinimumTLSVersion 字符串类型,指定TLS的最低版本。
  • NSExceptionRequiresForwardSecrecy 默认为YES,指定是否支持前向安全。(这个不了解)。
  • NSRequiresCertificateTransparency 默认为NO,指定是否支持证书透明,证书透明的大概意思是,如果服务端的证书被中间人攻击获取到了,在支持CertificateTransparency的情况下,中间人攻击仍然不能左右客户端的数据。

举个例子:

<key>NSAppTransportSecurity</key>
	<dict>
		<key>NSExceptionDomains</key>
		<dict>
			<key>domain-i-control.example.com</key>
			<dict>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<false/>
				<key>NSExceptionRequiresForwardSecrecy</key>
				<false/>
				<key>NSExceptionMinimumTLSVersion</key>
				<string>TLSv1.2</string>
			</dict>
			<key>other-domain-i-control.example.com</key>
			<dict>
				<key>NSExceptionAllowsInsecureHTTPLoads</key>
				<true/>
				<key>NSExceptionRequiresForwardSecrecy</key>
				<false/>
				<key>NSExceptionMinimumTLSVersion</key>
				<string>TLSv1.2</string>
				<key>NSIncludesSubdomains</key>
				<false/>
			</dict>
		</dict>
	</dict>

示例解读:上述例子表示允许 域名domain-i-control.example.com 下,不支持HTTP请求,不支持前向安全,指定TLS的最低版本为TLSv1.2; 域名other-domain-i-control.example.com 下,支持HTTP请求,不支持前向安全,指定TLS的最低版本为TLSv1.2;包含子域名。

ATS有很多值供我们配置,那么我们应该怎么去确定设置哪些值比较合适呢。而且有的时候不仅仅是http的请求会出现问题,有时候https的请求也可能出现问题,有的时候可能我们使用有些SDK的时候没有看到错误日志信息,如果有相应的错误码信息,像NSURLErrorAppTransportSecurityRequiresSecureConnection=-1022 也可以在一定程度上认为是SDK的网络请求方面有问题。那么我们也可以考虑使用下边的命令来查看怎么设置ATS来解决。

Apple提供了一个命令 (/usr/bin/nscurl --ats-diagnostics [--verbose] URL) 便于我们确定如何设置ATS。

举个例子,如我们故意把so.com的https写作http。那么如果要加载so.com下的资源,我们就可以使用下列命令来查看ATS的设置方式。

使用示例: /usr/bin/nscurl --ats-diagnostics --verbose so.com

结果演示:

Starting ATS Diagnostics

Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://so.com.
A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.
================================================================================

Default ATS Secure Connection
---
ATS Default Connection
ATS Dictionary:
{
}
Result : PASS
---

================================================================================

Allowing Arbitrary Loads

---
Allow All Loads
ATS Dictionary:
{
    NSAllowsArbitraryLoads = true;
}
Result : PASS
---

================================================================================

Configuring TLS exceptions for so.com

---
TLSv1.3
ATS Dictionary:
{
    NSExceptionDomains =     {
        "so.com" =         {
            NSExceptionMinimumTLSVersion = "TLSv1.3";
        };
    };
}
2018-12-11 15:36:01.690 nscurl[4102:692806] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9800)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9800, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7ff1c2c16780 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9800, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9800}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://so.com/, NSErrorFailingURLStringKey=https://so.com/, _kCFStreamErrorDomainKey=3}
---

---
TLSv1.2
ATS Dictionary:
{
    NSExceptionDomains =     {
        "so.com" =         {
            NSExceptionMinimumTLSVersion = "TLSv1.2";
        };
    };
}
Result : PASS

输出结果中,我们可以选用Result : PASS 的提示内容进行ATS配置。