iOS 网络数据安全(防止抓包)

A.首先 浅谈http、https与数据加密

ip、端口、http协议

下面用一个例子来介绍客户端与服务器在应用层的通讯流程

  • 有两个很好的朋友,小客(客户端)和小服(服务端),他们经常需要写信交流,那么他们寄信需要哪些步骤呢?

  • 1.小客需要有自己的家庭住址(IP地址)和小服的家庭住址,这样邮差才知道把信寄到哪里,以及小服收到信之后,要把返回的信息寄回哪里。

  • 2.信中要包含小客要告诉小服的内容,这些内容通常以Json字符串的形式传播,Json因其轻便简单,已渐渐替代掉了XML,假设小客给小服写了一段话:你那边天气怎么样,他就可以包装一个json:{"question":"你那边天气怎么样"},写到信中发送给小服。

  • 3.邮差拿到这封信就可以根据对方的ip地址,寄给小服,小服收到信之后,通过来源的ip地址就可以将自己的回答{"answer":"今天是晴天"}通过邮差返回给小客,整个通过寄信交流的流程就结束了。

  • 我们来仔细思考上述流程,在上述流程中,小客给小服寄信就是【请求】,小服回复小客就是【响应】,在http中,他们总是一对存在的。

  • 【什么是内网(局域网)?】小客和小服所在的小区,就可以理解为两个不同的内网,我们现实生活中一般通过路由器连接至外网(公网),这里的路由器可以理解为小区大门,所有的信都要从大门寄往其他的小区,大门外就是【因特网】,因特网把各个小区连接到了一起。

  • 【什么是IP地址?】IP地址可以看作是每个网络参与者的唯一地址,就像现实生活中我们的收件地址一样,包含省市区详细地址门牌号等信息,这样才可以精确地信寄到目的地,但是如果是在局域网中呢,如果收件人和寄件人身处通过小区,那他们的ip地址其实只需要包含楼栋和门牌号即可,如(5#1004),ip地址亦然。IPv4地址中预留了3个IP地址段,作为私有地址,共家庭、企业、学校等内部组网使用,最常见的是C类地址段192.168.0.0-192.168.255.255192.168.1.1是大多数路由器的ip地址,也就是小区大门的ip地址,这样就可以连接192.168.1.1(不包含)-192.168.1.255共254台设备,一般家庭路由器使用已经足够了。这样就会导致不同的局域网,可能有相同的ip地址,就像不同的小区都有(5#1004)一样,但是信息仍然可以准确送达,正是因为192.168.x.x是局域网地址,如果需要跨小区交流还需要公网地址。

  • 【什么是端口?】端口是服务端的概念,如果ip地址指的是家庭住址的话,那么端口就是这个家中你需要把信送达的那个人。

  • 什么是域名?ip地址是一串数字加点拼接的地址,用户通常很难记住,域名就相当于是ip地址的别名,帮助用户更好地记住需要访问网站的地址,www.baidu.com 就比14.215.177.38直观得多。

  • 【什么是域名解析?】域名解析值的是从域名解析到ip地址的过程,域名解析是由DNS服务器完成的,当我们访问www.baidu.com时,浏览器会像DNS服务器提交域名,DNS服务器返回这个域名的ip地址给浏览器,我们看到的是通过域名访问,实际上所有的通讯都是通过ip进行的,但是这就引发了一个常见的问题,就是DNS劫持,黑客通过DNS缓存感染或信息劫持等方式,将错误的ip放回给用户,导致用户被引导到其他不良网站,或者插入广告等不法目的。

  • 【什么是请求头、响应头、请求体、响应体】?请求头用于携带附加的信息,GET请求和POST请求都包含请求头,请求头通常包含:【Host】目标地址的地址+端口;【User-Agent】浏览器的类型;【Content-Length】请求消息内容的长度;【Content-type】请求消息内容的类型,如application/x-www-form-urlencodedapplication/json;等。

http传输安全

在上个例子中,小客和小服在沟通中存在一个很大的安全隐患,假设邮差在送信过程中被人拦截下来,并篡改了信的内容,这样就会导致隐私泄露和信息被恶意修改问题,并且小客和小服完全不知道发生了什么。我们如果使http请求更安全呢?

使用http请求真的那么不安全吗,攻击者到底是如何拦截我发出的请求的?

  • 请求的传输就和上方类比的“送信”是相似的,我们的手机、电脑发送的网络请求送设备的网卡发出后,一般第一步进过的就是路由器(网关),由路由器将请求传递到外网中。

  • 【抓取途径路由器的流量】当你连接他人的路由器,那么你发送的所有请求都会经过这个路由器,最便捷的可以通过路由器系统自带的tcpdump抓取数据帧,也可以将路由器镜像到电脑,通过Wireshark对电脑的网卡进行抓包,方式林林总总,但都能达到同一个目的,抓取通过这个路由器的流量或是篡改数据。

  • 【ARP欺骗】了解什么是ARP需要我们先知道什么是ip和MAC地址的关系,当我们的设备联网时会获得一个在这个局域网内唯一的ip地址,如果没有指定ip地址,我们的设备每次联网ip地址都可能发生改变,因此ip地址仅代表这个时候,这台设备在网络中的位置,它只代表一个别名,MAC地址也叫作物理地址,类似设备的身份证号,是设备的唯一标识,不管在何种网络环境下,MAC地址都是不会改变的。ARP协议用于将IP地址转换为MAC地址。当局域网内的两台设备需要通讯,表面上看它们是通过ip知道对方的位置进行通讯,实际上它们需要先根据ip找到对方的MAC地址,网络设备是通过MAC地址进行通讯的,而非ip地址。使用ARP欺骗的攻击者通过伪造数据包ARP报文,向局域网内的网络设备广播,将自己的MAC地址伪装成网关的MAC地址,使得局域网内的网络设备的ARP缓存表中网关IP对应的MAC地址变为自己设备的MAC地址,此时其他设备的所有请求都将发往攻击者的设备上,若攻击者对这些请求转发至网关或外网,则请求正常,否则其他设备将全部断网。

    攻击者通过ARP欺骗将自己的设备伪造成网关,使得连接这个路由器的所有网络设备的请求都经过自己的设备,通过这个方法,攻击者无需是路由器的所有者,也无需或者路由器的访问权限即可捕获甚至修改局域网内网络设备的所有请求。

  • 【用户自己进行抓包】用户可以通过代理抓包(如Charles,Burp等工具),或WireShark等对自己设备安装的应用程序或网页进行抓包,若开发者未对数据进行加密,则用户可以轻易对数据进行篡改,例如有一款打卡App,通过获取用户当前的经纬度并上传至服务器以判定该用户是否在规定范围内打卡,若请求数据为明文的,则用户可以修改经纬度并提交给服务器,服务器仍然会认可这个打卡操作,这样打卡就被轻松破解了。

【数据加密】

  • 假设小客给小服发了一句 hello,并且他们制定了一个加密规则,那就是对他们之间的聊天进行加密,以防止聊天消息被窃取和篡改,这个加密规则是:把明文中的每一个字母后移一位,生成密文,这样就生成了密文:ifmmp,显然若信件途中被拦截下来,拦截者是看不懂内容的,如果它对内容进行篡改,小服拿到密文把每一位字母前移一位,会发现不是正常的单词,就会发觉信息已被篡改。
  • 但是生活中商业化的加密方式肯定比上述复杂得多,以下列举几个比较常用的加密方式
  • 【对称加密】如AES、DES加密,对称加密的意思是加密和解密的密钥都是一样的,例如使用密钥12345对字符串进行加密,那就需要使用密钥12345对这个字符串解密以获得明文,密钥就像是钥匙一样,可以加锁,也可以解锁。
  • 【非对称加密】如RSA、ECC椭圆曲线加密算法,非对称加密需要两个密钥(一对密钥),公钥:公开密钥,私钥:私有密钥,使用公钥对数据进行加密,只能使用对应的私钥才能解锁,私钥是保密的,公钥是公开的。
  • 【Hash算法】如MD5、SHA,MD5使用较为广泛,它可以将一个任意长度的字符串生成16字节的散列值,不同的字符串,哪怕只有一个字母不同,他们的散列值都是完全不同的,相同的字符串有相同的散列值,并且无法根据散列值倒推回原来的明文,但是因为使用MD5生成的密文与明文一一对应,因此只要知道这一对明文和密文,就可以轻松根据密文推出明文,如“123”MD5加密后为的32位密文为“202cb962ac59075b964b07152d234b70”,那已知密文“202cb962ac59075b964b07152d234b70”即可推出它的明文为123,即使近年已证明MD5已被攻破,并推出了MD6,但目前而言,MD5仍被广泛用于密码不可逆加密,请求验签等。

【数据加密示例】

  • 【AES加密】已知明文{"data":"hello"},且客户端与服务端私下约定好key为12345,则使用AES加密出来的密文为(U2FsdGVkX189vc7hUqp6f8FhQMMUMI9GFiWOtChSEM3u4jDRLDJiwm6lee3MjmqT),则拦截者必须知道双方约定好的密钥才能获得明文,如果拦截者对密文进行篡改,服务端则无法正常解析,篡改内容不会被信任。

  • 【RSA加密】已知明文{"data":"hello"},客户端保存这公钥12345,服务端保存私钥09876,则使用公钥加密的密文只能通过私钥来解密,且由于公钥可以根据私钥计算出来,因此理论上只需要私钥就可以进行加解密,但是由于很多情况下私钥放在客户端并不安全,因此一般是客户端存放公钥,服务端存放私钥。

  • 【MD5加密】由于MD5加密是不可逆的,因此一般不会直接对数据进行加密,因为客户端使用MD5对整个数据进行加密了,服务端也无法获取明文,因此MD5加密一般用于用户密码加密和验签,为了提高MD5的安全性,一般在使用MD5的时候会进行“加盐”操作,例如明文是12345,盐是sajhdakj,那么实际上就是将12345sajhdakj进行MD5加密,这样攻击者很难通过密文倒推出明文,如今很多网站都可以解密MD5,实际上绝大多数都是存储着明文和密文的键值对,以便第一时间通过密文倒推明文。

    【用户密码加密】假设用户进行登录操作,发送请求{"account":"123","pwd":"456"},若此请求被攻击者拦截,攻击者可以轻松获得其密码并登录其账号,更为严重的事,很多用户不同平台使用的密码相同,这样就会极大地危害用户信息安全,因此一般都会对用户的密码“456”进行加盐的MD5操作,这样攻击者拿到密文无法推出明文,也就保证了用户的账户安全,对于服务端来说,其实没有必要得知用户密码的明文,只要在注册的时候将用户密码的密文存到数据库中,在用户登录的时候比较密文即可。因此大多数网站都无法提供密码找回服务(因为他们也不知道用户的密码明文是什么),只能提供密码重置服务。

    【请求验签】假设用户进行登录操作,发送请求{"account":"123","pwd":"456"},通过将json转化为特定的格式,如account=123&pwd=456(一般会拼接一段固定的字符串【盐】),并对其进行md5加密,将加密后的MD5值放在sign字段中,就得到{"account":"123","pwd":"456","sign":"MD5加密后的值"},服务器拿到这个json的时候,将sign以外的key和value进行与客户端相同的加密操作,并比对加密出来的结果与sign内容的值,若相同,则可以认为account和pwd未被篡改,可信任这个请求,若不同,则代表必然有信息被篡改,则废弃这个请求。

【https】

  • https是指http+SSL/TLS协议,在传输过程中对数据进行加密,开发者无法自己设定加密逻辑

  • 【https加密大致流程】服务端与客户端通过https进行通讯,数据加密使用到了非对称加密(一般是RSA)和对称加密,大致流程如下:

    1.客户端向服务端发起https请求。

    2.服务端将之前生成的一对RSA密钥中的公钥返回给客户端。

    3.客户端随机生成一个对称加密(如AES)的key(密钥),并通过服务器提供的RSA公钥,对这个key进行加密,将加密后的对称加密key发送给服务端。

    4.服务端拿到这个对称加密后的key,并通过自己的私钥对这个密文进行解密,拿到与客户端进行对称加密通讯的明文key。

    5.服务端与客户端通过这个key加密密文进行通信。

  • 【疑问】为什么这个流程这么繁琐,服务端直接把对称加密的密钥给客户端不就可以了吗?答:服务端把对称加密密钥给客户端的途中,可能被中间人拦截,中间人获得对称加密的key之后,加密形同虚设。

    上述流程真的就万无一失了吗?显然还有很大的漏洞!服务端将RSA非对称加密的公钥发送给客户端的途中,中间人可以将公钥拦截下来,并提前自己创建一对RSA密钥,将自己的RSA公钥发送给客户端,客户端通过这个被篡改的RSA公钥对自己随机生成的对称加密密钥进行加密后,发送给服务端,在这个途中,中间人可以将此密文拦截下来,并通过自己的RSA私钥进行解密(因为客户端实际上是用自己的公钥加密的),此时中间人可以拿到明文的对称加密密钥,此时中间人通过先前截获的服务端的RSA公钥对此对称密钥进行加密,并发送给服务端,服务端通过自己的RSA私钥可以获取到对称加密密钥的明文,此时中间人已经悄悄获取了二者将来进行的对称加密通讯的密钥,就可以轻松解密和篡改二者的通讯信息了。

  • 【如何解决上述问题】实际上https通讯过程中还有一个极为重要的角色:CA(Certificate Authority)数字证书颁发机构,CA实际上是指多个权威的证书颁发机构,他们会生成一对公钥和私钥,并将公钥存储于操作系统和浏览器中,通过CA的介入可以完美解决,CA介入以后的加密流程如下:

    1.客户端向服务端发起https请求。

    2.服务端将之前生成的一对RSA密钥中的公钥(通过CA的RSA私钥加密后),返回给客户端。

    3.客户端拿到加密后的服务器公钥,通过浏览器或系统中预先安装好的公钥进行解密,若可以解密,则https请求可以继续进行,客户端顺利拿到服务器公钥的明文,并使用这个公钥对随机生成的对称加密密钥加密,将加密后的对称加密key发送给服务端。若无法解密,则代表公钥被篡改,https请求终止,且浏览器会显示warning。

    4.服务端拿到这个对称加密后的key,并通过自己的私钥对这个密文进行解密,拿到与客户端进行对称加密通讯的明文key。

    5.服务端与客户端通过这个key加密密文进行通信。

    6.此时这个加密后的公钥若被中间人拦截,并且由于CA的公钥是公开的,因此中间人可以解密并获取服务器的公钥明文,但是他无法将其替换为自己的公钥,因为替换之后,他需要CA的私钥来对自己的公钥来进行加密,否则直接将明文的公钥或者是使用他人私钥加密后的公钥提交给客户端,由于客户端中没有可以解密的公钥,https请求将终止。

  • 【另一个疑问】为什么大多数的抓包工具,在我的手机安装一个根证书并让我信任后,就可以抓到https请求?

    通过以上的加密流程分析,可以很快地解决上述问题。

    1.安装根证书并信任,实际上就是让系统和浏览器认定所安装的根证书的CA身份,根证书中包含着一对公钥和私钥。

    2.安装根证书后,中间人(抓包程序)会先对服务端返回给客户端的经过CA私钥加密后的服务端公钥,通过CA公钥进行解密,此时就拿到了服务端公钥的明文,此时中间人通过自己创建的一个证书(证书中包含私钥和公钥,这个证书和让用户安装在手机上的根证书一致),中间人将服务器的公钥篡改为自己的公钥,通过其中的私钥对这个公钥进行加密,并发送给客户端,客户端拿到这个被篡改后的、使用中间人私钥加密的“服务端公钥”,在受信任的证书中查找可以用于解密的公钥,因为之前用户已经信任了这个根证书,因此此公钥可以顺利被解密。

    3.客户端随机生成一个对称加密(如AES)的key(密钥),并通过“服务器提供的RSA公钥”(已被篡改为中间人的公钥),对这个key进行加密,将加密后的对称加密key发送给服务端。

    4.中间人获取到这个已被公钥加密的对称加密的key,因此加密这个对称加密密钥的公钥,是自己的公钥,因此可以通过与之配对的私钥解密,成功获取对称加密密钥明文。

    5.中间人通过服务端的公钥,对这个对称加密密钥进行加密,并发送给服务端,服务端通过私钥对这个对称加密密钥解密,获取对称加密密钥明文,服务端与客户端之间的通讯使用这个对称加密进行,但是实际上,这个对称加密key已被中间人成功解密,二者通讯信息一览无遗,中间人可以轻松拦截和篡改https请求内容。

    信任根证书意味着信任根证书下的所有证书,此操作会极大影响用户的信息安全!

  • 【如何避免用户抓取https请求】SSL Pinning:一些应用将服务端的证书直接打包到App中,可以在https建立连接时对比本地证书与服务器返回的证书信息,若不匹配,则立即终止https连接。但是由于是在客户端判断证书一致性,因此可以通过hook的方式修改客户端的判断逻辑来绕过SSL pinning。

  • 以上https请求被拦截和篡改的前提是,用户主动在设备中安装并信任了根证书,因此在绝大多数情况下,https可以极大保障用户的信息安全,很多企业和机构开始强制要求开发者使用https进行通讯以保证用户的信息安全。

总结

  • http是我们日常开发中最常接触到的请求-响应协议,了解其大致原理可以帮助我们为用户提供更安全可靠的信息安全保障。由于本人能力和认知所限,以上内容纰漏之处在所难免,还望大家批评指正。

A 参考 https://github.com/SmileZXLee/aboutHttp.git

B.上面已经说到几个关于数据请求的安全处理.

1.服务端生成一对公钥和私钥把公钥交给客户端,
当客户端需要进行数据请求的时候,数据加密处理,客户端保存的公钥,对请求数据进行加密传给后台,后台利用私钥对请求解密,拿出参数进行处理后再加密返回给客户端.客户端对返回的数据进行解密然后进行界面的展示

NSString *publicKey = @"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0OiXHaees9Aj5h31YYGw5nfCUdS6MK0T5UrJAfIdwkUbadDOXclDVK+ftBMe+DVAn7xSORPi1cjiBBjU+lo/hmNGoDWQGgxr/LAkaJz3/A1Sv+S1d3deTc6SFN+toDQbpsx3jYOUrJM1B8olUI1a9f+DgzkF/sIKJ7V4Wh7XtlQIDAQAB";
    //私钥
    NSString *privateKey = @"MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALQ6Jcdp56z0CPmHfVhgbDmd8JR1LowrRPlSskB8h3CRRtp0M5dyUNUr5+0Ex74NUCfvFI5E+LVyOIEGNT6Wj+GY0agNZAaDGv8sCRonPf8DVK/5LV3d15NzpIU362gNBumzHeNg5SskzUHyiVQjVr1/4ODOQX+wgontXhaHte2VAgMBAAECgYAljox63sXpk70fCq4DMay74P7WYQj/KrEn56S/rXOn8I48TcTGhYr0sT6WdM2O/EU83SSCdTsCzLebo4iK72Mx/VI1alKWSfyncfXi51gZePpgVaudTG9kcI8sszRG+P7zfPptr4HxZ7X2LveJy5myImqQomESEUvDVHdZCtIIgQJBANxT7Bev+LA+jH9gSV4Uc14B5YYabpLso39t/uhTaOdVNxV2eV2UAF1PGg2R3IMwlzHcjRkDDpBiWuXaxbTKCaECQQDRaCUb7J1mPDBrEIi1Aupho16b3Sy82vzZ9WGLgMl+eMKbSy0rAdJA10CtNKL2Gq7EGNoN4CpDPPulJTmm6Cd1AkAajH5BaHHmAtN5McgFbx9rr3zRyPOT/rHA1CdIJWzZmzoU+v6q2P+mPrbb9byFjmBZoMLbxbOGkGN1mQQDweihAkEAkAJ9Mr0AaeSOr7KJMWK16Tu+vpXWRHKdXQ9Ba/y/lThbLQ0AHQl9nJXrprICOBmVgspMeypkJiV0Mdht03joWQJAaF8kDoCNkpp++6aqVbqFBYysiW83AiHgL0JA5dhQ2XzIFYZIpLOsM+Je4yw9ppQ76DqePg6pqRKjR6m9Gatn+A==";
    //测试要加密的数据
    NSString *sourceStr = @"iOS端RSA加密";
    //公钥加密
    NSString *encryptStr = [RSA encryptString:sourceStr publicKey:publicKey];
    //私钥解密
    NSString *decrypeStr = [RSA decryptString:encryptStr privateKey:privateKey];
    
    NSLog(@"公钥加密私钥解密后的数据 %@",decrypeStr);
    
    //私钥加密
    NSString *encryptStr1 = [RSA encryptString:sourceStr privateKey:privateKey];
    //公钥解密
    NSString *decrypeStr1 = [RSA decryptString:encryptStr1 publicKey:publicKey];
    NSLog(@"私钥加密公钥解密后的数据 %@",decrypeStr1);

2.其实第一个很多时候就可以了.对于复杂的APP,有很多接口是不用加密的.这样就出现了防代理的模式:

#pragma mark - 初步方案,判断是否设置代理,如果设置了代理就视为在抓包

- (BOOL)getProxyStatus {

    NSDictionary*proxySettings =  (__bridgeNSDictionary*)(CFNetworkCopySystemProxySettings());

    NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:@"http://www.baidu.com"]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));

    NSDictionary*settings = [proxiesobjectAtIndex:0];

    NSLog(@"host=%@", [settingsobjectForKey:(NSString*)kCFProxyHostNameKey]);

    NSLog(@"port=%@", [settingsobjectForKey:(NSString*)kCFProxyPortNumberKey]);

    NSLog(@"type=%@", [settingsobjectForKey:(NSString*)kCFProxyTypeKey]);

    if([[settingsobjectForKey:(NSString*)kCFProxyTypeKey]isEqualToString:@"kCFProxyTypeNone"]){

        //没有设置代理

        returnNO;

    }else{

        //设置代理了

        returnYES;

    }

}

这里用的CFNetwork.framework框架,进行代理的获取.
这种接口的处理对于用户开VPN的情况下很多时候是有错误的效果.他其实没有代理想抓你包的意思.但是开的VPN会被认为是代理了,导致无法使用APP.

3.SSL Pinning

通过对服务端生成的.cer证书进行域名校验,服务器通过.crt证书导出.cer放到客户端进行处理.
NSURLSession处理

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {

    //得到远程证书
    SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
    SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
    //设置ssl政策来检测主域名
    NSMutableArray *policies = [NSMutableArray array];
    [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)];
    //验证服务器证书
    SecTrustResultType result;
    SecTrustEvaluate(serverTrust, &result);
    BOOL certificateIsValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
    //得到本地和远程证书data
    NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
    
    BOOL allChrls = NO;
    allChrls = DebugNet;
    if (allChrls) {
        NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
    }else{
        NSString *pathToCer = [[NSBundle mainBundle] pathForResource:@"xiaoqi" ofType:@"cer"];
        NSData *localCertificate = [NSData dataWithContentsOfFile:pathToCer];    //检查
        if ([remoteCertificateData isEqualToData:localCertificate] && certificateIsValid) {
            self.allowTask = YES;
            NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        }else {
            self.allowTask = NO;
            [_dataTask cancel];
            [_downloadTask cancel];
            [_uploadTask cancel];
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,NULL);
        }
    }

    
}


或者是AF里面自定义

// 自定义安全策略
+ (AFSecurityPolicy *)customSecurityPolicy {
    
    // 获取证书
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"xiaoqi" ofType:@"cer"];
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];
    NSSet *pinnedCertificates = [[NSSet alloc] initWithObjects:certData, nil];

    /*
     安全模式
     AFSSLPinningModeNone:完全信任服务器证书;
     AFSSLPinningModePublicKey:只比对服务器证书和本地证书的Public Key是否一致,如果一致则信任服务器证书;
     AFSSLPinningModeCertificate:比对服务器证书和本地证书的所有内容,完全一致则信任服务器证书
     */
    AFSecurityPolicy *securityPolicy =
    [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey
                     withPinnedCertificates:pinnedCertificates];
    
    // allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
    // 如果是需要验证自建证书,需要设置为YES
    securityPolicy.allowInvalidCertificates = YES;
    
    /*
    validatesDomainName 是否需要验证域名,默认为YES;
    假如证书的域名与你请求的域名不一致,需把该项设置为NO;
    如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
    置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。
    因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;
    当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
    如置为NO,建议自己添加对应域名的校验逻辑。
     */
    securityPolicy.validatesDomainName = YES;
    
    return securityPolicy;
}

证书会失效,证书由于是服务端生成的根据域名来的所以一般最长的是一年的证书.所以不能忘了换,不然可能会对接口请求产生问题.
那么还有没有不用证书来校验的方式呢?还能防止抓包呢?

苹果官方文档

CFNetWork

This property controls which proxy tasks within sessions based on this configuration use when connecting to remote hosts.
The default value is NULL, which means that tasks use the default system settings.

这个属性可以设置网络代理,默认值是 NULL,使用系统的代理设置。
configuration.connectionProxyDictionary = @{};

#import "NSURLSession+SafeHttpProxy.h"
#import "SafeURLProtocol.h"
#import 
static BOOL isDisableHttpProxy = YES;
@implementation NSURLSession (SafeHttpProxy)
+(void)load{
    [super load];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [NSURLProtocol registerClass:[SafeURLProtocol class]];
        Class class = [NSURLSession class];
        [self swizzingMethodWithClass:class orgSel:NSSelectorFromString(@"sessionWithConfiguration:") swiSel:NSSelectorFromString(@"Safe_sessionWithConfiguration:")];
        [self swizzingMethodWithClass:class orgSel:NSSelectorFromString(@"sessionWithConfiguration:delegate:delegateQueue:") swiSel:NSSelectorFromString(@"Safe_sessionWithConfiguration:delegate:delegateQueue:")];
    });
}
+(void)disableHttpProxy{
    isDisableHttpProxy = YES;
}
+(void)enableHttpProxy{
    isDisableHttpProxy = NO;
}
+(NSURLSession *)Safe_sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
                                    delegate:(nullable id)delegate
                               delegateQueue:(nullable NSOperationQueue *)queue{
    if (!configuration){
        configuration = [[NSURLSessionConfiguration alloc] init];
    }
    if(isDisableHttpProxy){
        configuration.connectionProxyDictionary = @{};
    }
    return [self Safe_sessionWithConfiguration:configuration delegate:delegate delegateQueue:queue];
}

+(NSURLSession *)Safe_sessionWithConfiguration:(NSURLSessionConfiguration *)configuration{
    if (configuration && isDisableHttpProxy){
        configuration.connectionProxyDictionary = @{};
    }
    return [self Safe_sessionWithConfiguration:configuration];
}

+(void)swizzingMethodWithClass:(Class)cls orgSel:(SEL) orgSel swiSel:(SEL) swiSel{
    Method orgMethod = class_getClassMethod(cls, orgSel);
    Method swiMethod = class_getClassMethod(cls, swiSel);
    method_exchangeImplementations(orgMethod, swiMethod);
}

@end

https://github.com/frankKiwi/FNKSafaNet.git

以上四个方案,建议是第一个加密 和 第四个结合处理.第四个对于H5页面的网络请求还是可以抓到的.

做个记录两年后可能就忘了.

IOS七层协议:
从下到上:
a.物理层:传输的是比特流,网卡位于这层。
b.数据链路层:本层传输的是帧;本层主要定义了如何格式化数据,错误检测。交换机位于本层
c.网络层:本层传输的是数据包,路由器位于本层。本层协议是IP协议(Internet Protocol Address),主要功能是路由选择最短路径,将数据包从发送端路由到接收端
d.传输层:协议有TCP(传输控制协议)/UDP(用户数据报协议);主要是控制重传、数据分割之类的,主要是解决数据之间的传输,和传输质量。是IOS最核心的一层,其中TCP协议是最重要的协议
e.会话层:不同机器之间建立会话
f.表示层:解决不同系统之间的语法问题
g.应用层:应用网络中发送数据:需要注意Http协议(超文本传输协议),Https(超文本传输安全协议)

TCP/IP四层模型
与IOS七层模型相同,从下往上依次为:
a.链路层(物理层+数据链路层),
b.网络层(IP),
c.传输层(TCP),
d.应用层(应用层+表示层+会话层)(HTTP)。

你可能感兴趣的:(iOS 网络数据安全(防止抓包))