我正在使用UIWebView在我的iOS应用中加载网站。在更新到iOS 9并相应地构建应用程序之后,该网站仍然可以从我的服务器上正常加载,但是无法从cdnjs加载bootstrap的css和js文件(例如https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css)。

我认为这与新的ATS有关,但到目前为止,我仍无法弄清为什么它无法从cdnjs加载文件(它们支持TLS 1.2和大量现代密码),而且我还没有El Capitan,可以使用nscurl进行测试cdnjs(请参阅https://developer.apple.com/library/prerelease/ios/technotes/App-Transport-Security-Technote/)。

最佳答案

我在El Capitan上为您运行了一个nscurl。根据结果​​,问题在于cdnjs不会支持Forward Secrecy,因此您必须为cdnjs.cloudflare.com添加一个例外,并将NSAppTransportSecurity设置为false。

更新:以下几行就足够了:

  <key>NSAppTransportSecurity</key>
  <dict>
    <key>NSExceptionDomains</key>
    <dict>
      <key>cdnjs.cloudflare.com</key>
      <dict>
        <key>NSExceptionRequiresForwardSecrecy</key><false/>
      </dict>
    </dict>
  </dict>

有关更多详细信息,请参见下面的日志:
nscurl --ats-diagnostics --verbose https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css
Starting ATS Diagnostics

Configuring ATS Info.plist keys and displaying the result of HTTPS loads to https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css.
A test will "PASS" if URLSession:task:didCompleteWithError: returns a nil error.
================================================================================

Default ATS Secure Connection
---
ATS Default Connection
ATS Dictionary:
{
}
2015-09-29 14:59:16.983 nscurl[490:10186] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrust 0x7f7f89e052f0 [0x7fff7c50f890]>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
    "<SecCertificate 0x7f7f89e04570 [0x7fff7c50f890]>",
    "<SecCertificate 0x7f7f89e04950 [0x7fff7c50f890]>"
), NSUnderlyingError=0x7f7f8b8059b0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrust 0x7f7f89e052f0 [0x7fff7c50f890]>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
    "<SecCertificate 0x7f7f89e04570 [0x7fff7c50f890]>",
    "<SecCertificate 0x7f7f89e04950 [0x7fff7c50f890]>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorFailingURLStringKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorClientCertificateStateKey=0}
---

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

Allowing Arbitrary Loads

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

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

Configuring TLS exceptions for cdnjs.cloudflare.com

---
TLSv1.2
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionMinimumTLSVersion = "TLSv1.2";
        };
    };
}
2015-09-29 14:59:17.140 nscurl[490:10186] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrust 0x7f7f89f136f0 [0x7fff7c50f890]>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
    "<SecCertificate 0x7f7f89f12b20 [0x7fff7c50f890]>",
    "<SecCertificate 0x7f7f89f12d60 [0x7fff7c50f890]>"
), NSUnderlyingError=0x7f7f8b804160 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrust 0x7f7f89f136f0 [0x7fff7c50f890]>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
    "<SecCertificate 0x7f7f89f12b20 [0x7fff7c50f890]>",
    "<SecCertificate 0x7f7f89f12d60 [0x7fff7c50f890]>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorFailingURLStringKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorClientCertificateStateKey=0}
---

---
TLSv1.1
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionMinimumTLSVersion = "TLSv1.1";
        };
    };
}
2015-09-29 14:59:17.170 nscurl[490:10186] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrust 0x7f7f89c2a2a0 [0x7fff7c50f890]>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
    "<SecCertificate 0x7f7f89c47750 [0x7fff7c50f890]>",
    "<SecCertificate 0x7f7f89c45c90 [0x7fff7c50f890]>"
), NSUnderlyingError=0x7f7f8b9029d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrust 0x7f7f89c2a2a0 [0x7fff7c50f890]>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
    "<SecCertificate 0x7f7f89c47750 [0x7fff7c50f890]>",
    "<SecCertificate 0x7f7f89c45c90 [0x7fff7c50f890]>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorFailingURLStringKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorClientCertificateStateKey=0}
---

---
TLSv1.0
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionMinimumTLSVersion = "TLSv1.0";
        };
    };
}
2015-09-29 14:59:17.206 nscurl[490:10186] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
Result : FAIL
Error : Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrust 0x7f7f8b905230 [0x7fff7c50f890]>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, NSErrorPeerCertificateChainKey=(
    "<SecCertificate 0x7f7f8b904590 [0x7fff7c50f890]>",
    "<SecCertificate 0x7f7f8b9047d0 [0x7fff7c50f890]>"
), NSUnderlyingError=0x7f7f89d5fce0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrust 0x7f7f8b905230 [0x7fff7c50f890]>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9802, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9802, kCFStreamPropertySSLPeerCertificates=(
    "<SecCertificate 0x7f7f8b904590 [0x7fff7c50f890]>",
    "<SecCertificate 0x7f7f8b9047d0 [0x7fff7c50f890]>"
)}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorFailingURLStringKey=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css, NSErrorClientCertificateStateKey=0}
---

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

Configuring PFS exceptions for cdnjs.cloudflare.com

---
Disabling Perfect Forward Secrecy
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionRequiresForwardSecrecy = false;
        };
    };
}
Result : PASS
---

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

Configuring PFS exceptions and allowing insecure HTTP for cdnjs.cloudflare.com

---
Disabling Perfect Forward Secrecy and Allowing Insecure HTTP
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionAllowsInsecureHTTPLoads = true;
            NSExceptionRequiresForwardSecrecy = false;
        };
    };
}
Result : PASS
---

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

Configuring TLS exceptions with PFS disabled for cdnjs.cloudflare.com

---
TLSv1.2 with PFS disabled
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionMinimumTLSVersion = "TLSv1.2";
            NSExceptionRequiresForwardSecrecy = false;
        };
    };
}
Result : PASS
---

---
TLSv1.1 with PFS disabled
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionMinimumTLSVersion = "TLSv1.1";
            NSExceptionRequiresForwardSecrecy = false;
        };
    };
}
Result : PASS
---

---
TLSv1.0 with PFS disabled
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionMinimumTLSVersion = "TLSv1.0";
            NSExceptionRequiresForwardSecrecy = false;
        };
    };
}
Result : PASS
---

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

Configuring TLS exceptions with PFS disabled and insecure HTTP allowed for cdnjs.cloudflare.com

---
TLSv1.2 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionAllowsInsecureHTTPLoads = true;
            NSExceptionMinimumTLSVersion = "TLSv1.2";
            NSExceptionRequiresForwardSecrecy = false;
        };
    };
}
Result : PASS
---

---
TLSv1.1 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionAllowsInsecureHTTPLoads = true;
            NSExceptionMinimumTLSVersion = "TLSv1.1";
            NSExceptionRequiresForwardSecrecy = false;
        };
    };
}
Result : PASS
---

---
TLSv1.0 with PFS disabled and insecure HTTP allowed
ATS Dictionary:
{
    NSExceptionDomains =     {
        "cdnjs.cloudflare.com" =         {
            NSExceptionAllowsInsecureHTTPLoads = true;
            NSExceptionMinimumTLSVersion = "TLSv1.0";
            NSExceptionRequiresForwardSecrecy = false;
        };
    };
}
Result : PASS
---

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

10-07 19:56