美文网首页iOS工作系列iOS开发iOSer 干货部落
正确使用AFNetworking的SSL保证网络安全

正确使用AFNetworking的SSL保证网络安全

作者: 圣迪 | 来源:发表于2015-10-31 21:23 被阅读23314次

    Reference: https://www.cainwang.cn/afnssl/  
    AFNetworking, iOS开发中,以其优雅的结构设计和简便的调用方式,使其成为了最流行的网络开源库之一(另一个应该算是ASI了,但经久失修不维护的原因,已经不是首选)。
      我们在大多数情况下,都能够正确使用AFNetworking的功能,但在网络安全日趋严峻的今天,加入SSL使用HTTPS已经成为了很多大中型网站的首选;这点在国外尤其流行,例如Google已经全站HTTPS。
      本文便主要描述了如何正确使用AFNetworking中的SSL功能。详细步骤如下:
    1、获取到站点的证书:
      我们可以使用以下openssl命令来获取到服务器的公开二进制证书(以google为例):

    "openssl s_client -connect www.google.com:443 </dev/null 2>/dev/null | openssl x509 -outform DER > https.cer"
    

    冒号中的为命令主要部分。该条命令将会在当前路径下,形成google.com站点的公开二进制证书,命名为https.cer。您可以将www.google.com 替换成您自己的站点以此来获取您自己站点的https.cer。

    2、将证书放进我们的XCode项目工程中:


    Paste_Image.png

      如上图所示,将我们的https.cer拖到我们的工程Supporting Files中,把 Copy Items if needed 的勾选上。然后把您的Add to targets 选上,点击确定。就完成了证书的导入工作。

    3、在我们的代码中使用我们的cer
      AFNetworking中的AFSecurityPolicy是主要的类,我们可以这样来使用它(AFNetworking 2.6.0之前):

        AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    
        NSString *cerPath                = [[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"];
        NSData *certData                 = [NSData dataWithContentsOfFile:cerPath];
        AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init];
        [securityPolicy setAllowInvalidCertificates:NO];
        [securityPolicy setPinnedCertificates:@[certData]];
        [securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate];
        [securityPolicy setValidatesDomainName:YES];
        [securityPolicy setValidatesCertificateChain:NO];
    
        manager.securityPolicy = securityPolicy;
    

    解析:
    1)新建一个manager, 地球人都知道
    2)在mainBundle中寻找我们刚才拖进项目中的https.cer, 并且将相关的数据读取出来
    3)新建一个AFSecurityPolicy,并进行相应的配置
    4)将这个AFSecurityPolicy 实例赋值给manager

    也可以这样来使用:

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; 
    
    AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init]; 
    [securityPolicy setAllowInvalidCertificates:NO]; 
    [securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate]; 
    [securityPolicy setValidatesDomainName:YES];
    [securityPolicy setValidatesCertificateChain:NO]; 
    
    manager.securityPolicy = securityPolicy;
    

    这种方式比前面那种方式要更加简便一些,主要原因在于AFNetworking会自动去搜索mainBundle下的所有cer结尾的文件并放进内存中;再一一对比。因此在代码中可以省略不写。

    这样一个网络请求的https的安全策略就配置好了,接下来再说明一下几个AFSecurityPolicy相关的配置
    1> SSLPinningMode
    SSLPinningMode 定义了https连接时,如何去校验服务器端给予的证书。

    typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
        AFSSLPinningModeNone,
        AFSSLPinningModePublicKey,
        AFSSLPinningModeCertificate,
    };
    

    AFSSLPinningModeNone: 代表客户端无条件地信任服务器端返回的证书。
    AFSSLPinningModePublicKey: 代表客户端会将服务器端返回的证书与本地保存的证书中,PublicKey的部分进行校验;如果正确,才继续进行。
    AFSSLPinningModeCertificate: 代表客户端会将服务器端返回的证书和本地保存的证书中的所有内容,包括PublicKey和证书部分,全部进行校验;如果正确,才继续进行。

    2> allowInvalidCertificates
      allowInvalidCertificates 定义了客户端是否信任非法证书。一般来说,每个版本的iOS设备中,都会包含一些既有的CA根证书。如果接收到的证书是iOS信任的CA根证书签名的,那么则为合法证书;否则则为“非法”证书。
      allowInvalidCertificates 就是用来确认是否信任这样的证书的。当然,我们也可以给iOS加入新的信任的CA证书。iOS已有的CA根证书,可以在这里了解到:https://support.apple.com/en-us/HT204132

    3> pinnedCertificates
      pinnedCertificates 就是用来校验服务器返回证书的证书。通常都保存在mainBundle 下。通常默认情况下,AFNetworking会自动寻找在mainBundle的根目录下所有的.cer文件并保存在pinnedCertificates数组里,以校验服务器返回的证书。

    4> validatesDomainName
      validatesDomainName 是指是否校验在证书中的domain这一个字段。每个证书都会包含一个DomainName, 它可以是一个IP地址,一个域名或者一端带有通配符的域名。如*.google.com, www.google.com 都可以成为这个证书的DomainName。设置validatesDomainName=YES将严格地保证其安全性。

    5> validatesCertificateChain
      validatesCertificateChain 指的是是否校验其证书链。
      通常来讲,一个CA证书颁发机构有很多个子机构,用来签发不同用途的子证书,然后这些子证书又再用来签发相应的证书。只有证书链上的证书都正确,CertificateChain才算验证完成。以Google为例:

    Paste_Image.png

    从上图可以看到,Google.com的证书的根CA证书是GeoTrust Global CA; 而CA并没有直接给google.com签证书,而是先签名了Google Internet Authority G2, 然后G2再签名了google.com。这时候就需要设备中保存有Google Internet Authority G2证书才能通过校验。
      一般来讲,我推荐将validatesCertificateChain设置为NO,因为并不是太有必要做CertificateChain的校验。并且,在AFNetworking 2.6.0中,也正式将validatesCertificateChain拿掉了(https://github.com/AFNetworking/AFNetworking/blob/master/CHANGELOG.md), 其原因也同样为:There was no documented security advantage to pinning against an entire certificate chain。
      因此,在2.6.0之后,可以不管这个字段。而在此之前,从效率上来说,设定为NO会是个比较明智的选择。

    做好以上工作后,您应该就可以正常访问您自己的https服务器了。如果还是有问题请检查:
    (1)、HTTPS服务器的正确配置。一般来说,可以使用浏览器打开相同页面来查看浏览器上的小锁是否正常。
    (2)、是否https.cer正确打包进了项目中。查看第2步中的内容。
    (3)、其他。跟帖呗。有问题大家一起交流,共同进步:)

    这几个东西别看就这么一点,理解了好久才理清楚其中代表的含义。接下来打算再写几篇关于SSL的详细博文以纪念这些深度研究的时间。

    参考文档:
    1、https://github.com/AFNetworking/AFNetworking/blob/master/CHANGELOG.md
    2、http://nelson.logdown.com/posts/2015/04/29/how-to-properly-setup-afnetworking-security-connection/

    相关文章

      网友评论

      • Dee_Das:AFSSLPinningModeNone: 代表客户端无条件地信任服务器端返回的证书。
        -----------------------------------
        KIDDING ME ???
        翀鹰精灵:AFSSLPinningModeNone: 代表客户端无条件地信任服务器端返回的证书。
        这个是正取的么,不是不验证证书么?
      • farawei:有个问题咨询下:
        我随意去生成一个cer证书:(openssl s_client -connect www.google.com:443 </dev/null 2>/dev/null | openssl x509 -outform DER > https.cer)
        通过AF去使用(AFSSLPinningModeCertificate),都可以去使用https请求成功,所以说iOS客户端其实并没有验证域名与证书
        圣迪:@farawei 您可以看下AF的HTTPS 实现的源码
      • 鸿雁长飞光不度:请问如果证书过期了怎么办。换证书的时候是不是以前的用户都不能用了。
      • Tamp_:您好,如果我不添加证书直接用https,这样也可以用,但是这样的话https的安全性是不是就没有意义了呢?有一些不太知道的地方希望能向您请教一下,方便加个微信或者QQ吗
        Tamp_:或者如果方便的话,麻烦您加我也可以465821361.因为最近在看AF的源码,想了解的透彻一点
      • 52af03e56023:通过分析源代码
        - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
        forDomain:(NSString *)domain

        AFSSLPinningModeNone: 采用系统root证书验证服务器返回的证书。
        AFSSLPinningModePublicKey: 采用预埋证书【pinnedCertificates】校验服务器返回的证书公约。
        AFSSLPinningModeCertificate: 采用预埋证书【pinnedCertificates】校验服务器返回的证书。
        注意:
        validatesDomainName 是否校验证书域名
        allowInvalidCertificates 是否允许非法证书,基本模式预埋证书都是未经过第三方权威机构签名的证书 故使用 AFSSLPinningModePublicKey和 AFSSLPinningModeCertificate 时候 请将其设置成YES
        validatesCertificateChain 是否校验服务服务器证书签发root证书
        圣迪:@艾就一个字 赞!
      • LeoZzz:把证书放到项目里,证书过期了 。会不会影响 线上的使用?
        圣迪:@Joshua6 会。因此需要做好更新的策略
      • 可惜你不是我的双子座:您好,Terminating app due to uncaught exception 'Invalid Security Policy', reason: 'A security policy configured with `AFSSLPinningModeCertificate` can only be applied on a manager with a secure base URL (i.e. https)' 这是什么错误 af是最新的!
        pawn_cheng:请问解决了嘛?
      • LeoZzz:AFSSLPinningModeNone 感觉可以用这个 这样证书过期就不用从新发版了 而且前端不用配置太多
        圣迪:@Lucien_Bai AFSSLPinningModeNone 其实根本不校验cer证书的
        圣迪:@Sunshinking 这种形势并不校验证书,易收到MIMA
        5c6d09145665:我这边按照楼主的配置配完了,从网站上下的cer证书也没错,打开查看也是可以用的,
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone withPinnedCertificates:[NSSet setWithObject:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"https" ofType:@"cer"]]]];
        [securityPolicy setAllowInvalidCertificates:YES];
        [securityPolicy setValidatesDomainName:NO];
        self.manager.securityPolicy = securityPolicy;
        然后还是不行啊。。。。发生了ssl错误,无法建立与该服务器的安全连接。。。。碰到过这种情况么
      • 席列2:有个疑问,操作系统有根证书,然后又安装了charles的假证书。那么有没有办法,不导入证书,选择原有的根证书,而不是charles的假证书来验证,避免charles的中间人攻击?另外,为什么微信,支付宝等APP解压缩的时候,并没有看到有打包证书在app包内,作为锚点证书?
        圣迪:@席列2 微信自己实现了自己的一套SSL 的Infra,有自己的N次协议握手。支付宝没有研究过~~
      • KeepMoveingOn:您好,我想请问下如果导入的cer证书过期怎么办?那之前上架的版本都无法进行网络请求了?
        圣迪:@KeepMoveingOn 可以在自己的服务器上写一个服务来做这个事情。每次app启动时去咨询服务器是否有最新的证书
        KeepMoveingOn:@圣迪 :joy: 证书快过期了才发现这个坑,现在更新机制大概有几种?1.启动自动更新2.版本更新时。这两个更新我感觉都存在证书过期的风险,不知道博主有什么好的办法吗?
        圣迪:@KeepMoveingOn 所以需要做更新机制
      • 抱走_萝莉:好想学习 :+1:
        圣迪:@抱走_萝莉 同想继续学习
      • d086a77e02da:[securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate];
        这句话报错,怎么解决啊
        报错的内容是 No visible @interface for 'AFSecurityPolicy' declares the selector 'setSSLPinningMode:'
        Mr_FF:@圣迪 已经OK了,3Q
        圣迪:@Mr_FF 是不是各位AFN版本不对?未遇见过
        Mr_FF:我也遇到了同样的错误,请问解决了吗
      • dongwenbo:没太明白这里的安全策略
      • 536323b69f60:如果你有好的解决方法,请加我的QQ583067805,多谢多谢
      • 536323b69f60:楼主,我们使用AFN来进行https网络请求,会出现网络请求速度特别慢的情况,是为什么呢?
        圣迪:@浓墨散不开故人不再来 目前的网络监控可能能解决你的问题。

        网络请求阶段有 dns, tcp, ssl 以及response 等阶段。通过网络监控来看看到底哪个阶段出现了瓶颈,然后有针对性的优化即可。
      • 神采飞扬_2015:楼主:Error Domain=kCFErrorDomainCFNetwork Code=310 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2096, _kCFStreamErrorDomainKey=4} 这个是什么原因?一直没找到。
        love阿木蛋花:@神采飞扬_2015 服务器什么原因 是本地afn 配置的问题吧
        神采飞扬_2015:@love阿木蛋花 服务端的原因样
        love阿木蛋花:@神采飞扬_2015 找到什么原因了么
      • LoveY34:楼主:使用openssl获取证书的时候提示“unable to load certificate
        16560:error:0906D06C:PEM routines:PEM_read_bio:no start line:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-59.60.1/src/crypto/pem/pem_lib.c:648:Expecting: TRUSTED CERTIFICATE”,你知道是什么情况嘛?
        LoveY34:@拾荒少年v 我直接在浏览器里面打开服务器的地址,在浏览器里面找到证书的。
        xxxixxxx:@lovey1314 你解决了吗?
      • 幻想无极:摸摸大
      • 米苏芊:这是AF2.6之前的,现在最新的3.2中,我通过AFHTTPSessionManager来弄会报错:
        [__NSArrayM insertObject:atIndex:]: object cannot be nil'

        不知道为什么。。。。。
        圣迪:@米苏芊 对的,银行系统得自己实现双向验证
        米苏芊:@圣迪 解决了。是证书导出方式不正确,损坏了。放入Bundle了。
        另外我要补充一点:这种方式是AFNetworking自己实现的单方面的服务器证书验证
        双向验证需要自己重写验证回调函数
        圣迪:有没有把证书工钥放bundle?
      • c6e16b2e3a16:您好! 如果把证书添加到项目中,别人下载ipa 包解压就可以拿到证书,这样安全吗?
        圣迪:@hello_me 这里放进项目里的,是公钥。没有私钥,这东西基本就没什么作用的
      • zyg:Hello 请问 文中说 会再写几篇 SSL 请问开始了吗?
      • f7c081cbebac:如果是通过IP来访问也可以这么处理不?
        圣迪:@Daniel张丹 这取决于你的ssl证书是ip绑定还是域名绑定了
      • StoneLeon:对现在的开发还是很有用的。 :+1:
        圣迪:@StoneLeon 很高兴能帮到你:smile:
      • 小大大:肾神
      • A天天涨不停:大神,么么哒
        幻想无极:@水瓶座_iOSer 你在这里哦
      • Charse: :smile: 前排膜拜大神

      本文标题:正确使用AFNetworking的SSL保证网络安全

      本文链接:https://www.haomeiwen.com/subject/nuzfhttx.html