HTTP防劫持方案

DNS污染检测
被改标题
被挂黑链
被入侵
检测网站是否被劫持
网站打开速度检测
网站是否被黑
域名是否被墙      网站监控 http://www.iis7.com/b/wzjk/?inviteCode=496

HTTP劫持是在使用者与其目的网络服务所建立的专用数据通道中,监视特定数据信息,提示当满足设定的条件时,就会在正常的数据流中插入精心设计的网络数据报文,目的是让用户端程序解释”错误”的数据,并以弹出新窗口的形式在使用者界面展示宣传性广告或者直接显示某网站的内容。

什么是HTTP劫持

在运营商的路由器节点上,设置协议检测,一旦发现是HTTP请求,而且是html类型请求,则拦截处理。后续做法往往分为2种,1种是类似DNS劫持返回302让用户浏览器跳转到另外的地址,还有1种是在服务器返回的 HTML 数据中插入 js 或 dom 节点,从而使网页中出现自己的广告等等垃圾信息。
看图你就懂了一切(虽然被劫持我也很绝望)

URL Loading System

官方文档

 

1

2

3

 

The URL loading system is a set of classes and protocols that allow your app to access

content referenced by a URL. At the heart of this technology is the NSURL class, which

lets your app manipulate URLs and the resources they refer to.

 

官方配图About the URL Loading System

NSURLCache

NSURLCache 为 URL 请求提供了内存中以及磁盘上的综合缓存机制。 作为基础类库 URL Loading System 的一部分,任何通过 NSURLConnection 加载的请求都将被 NSURLCache 处理。

当一个请求完成得到来自服务器的Response,在本地保存作为cache。下一次同一个请求再发起时,本地保存的Response就会马上返回,不需要连接服务器。NSURLCache 会 自动 且 透明 地返回回应。

在NSURLConnection加载系统中,缓存被设计为request对象的一个属性,由NSURLRequest对象的cachePolicy属性指定。而在NSURLSession加载系统中,缓存被设计为 NSURLSessionConfiguration对像的一个属性,该属性所指定的策略被该session的所有request所共享。

作为一个Cache,它头文件中提供的方法并不复杂,就是基本的增删查改,(其中增和改可以算是一个功能,没有就增,改就是覆盖)。主要方法仅六个:

 

1

2

3

4

5

6

7

8

9

10

 

// 初始化方法

- (instancetype)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(nullable NSString *)path;

// 查询方法

- (nullable NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request;

// 存储方法

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request;

// 删除方法

- (void)removeCachedResponseForRequest:(NSURLRequest *)request;

- (void)removeAllCachedResponses;

- (void)removeCachedResponsesSinceDate:(NSDate *)date NS_AVAILABLE(10_10, 8_0);

 

当然,在项目中为了方便,我们一般都会实现一个子类,当系统调用URLCache的增删改查方法时,由子类来接管系统的URLCache功能。
NSURLCache的缓存策略,以及和HTTP header之间的关系,可以参考NSHipster NSURLCache文章

js签名防注入

这个方法主要用于资讯类App,就像文章开头那样的情况
步骤如下:
(1).发版前,在 bundle 中存一份最新的前端js文件
(2).后台在返回 js 文件 URL 的时候,对 js 文件内容进行 SHA-256 ,得到的 hash 值拼接到 js 的文件名中
(3).请求 js 资源文件时,在NSURLCache中的- (NSCachedURLResponse )cachedResponseForRequest:(NSURLRequest )request方法中拦截请求
(4).拦截请求之后,判断本地是否有缓存,如果有,则直接返回缓存文件包装成 response
(5).下载 js 资源时,走NSURLProtocol 代理方法- (void)connection:(NSURLConnection )connection didReceiveData:(NSData )data,对 data 进行SHA-256签名比对,如果签名一致,将 data 通过;如果签名不一致,代表 js 被污染,直接丢弃,从bundle取出本地预存的 js 文件返回回来。

基本逻辑如下(需要用到上文中NSURLCache实现的子类)
URLCache实现文件中:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

 

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {

NSString* ua = [request valueForHTTPHeaderField:@"User-Agent"];

if (!EmptyString(ua) && [ua lf_containsSubString:@"AppleWebKit"]) {

if ([CacheManager shouldVerifyHashCode:request]) { //包含64位hashcode的js css文件

// 取本地JS缓存

NSData *resultData = [CacheManager getJSCache];

if (resultData) {

NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:nil expectedContentLength:resultData.length textEncodingName:nil];

return [[NSCachedURLResponse alloc] initWithResponse:response data:resultData];

} else {

return nil;

}

}

return [super cachedResponseForRequest:request];

}

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {

NSString* ua = [request valueForHTTPHeaderField:@"User-Agent"];

if ([CacheManager shouldVerifyHashCode:request] && [ua lf_containsSubString:@"AppleWebKit"]) {

// 将请求回来的,并且通过验证的新js放到缓存中

[[CacheManager defaultManager] storeCachedResponse:cachedResponse forRequest:request])

return;

}

[super storeCachedResponse:cachedResponse forRequest:request];

}

 

自定义的NSURLProtocol子类:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

 

// 初始化方法

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {

NSString *ua = [request valueForHTTPHeaderField:@"User-Agent"];

if ([CacheManager shouldVerifyHashCode:request] && [ua lf_containsSubString:@"AppleWebKit"]) {

// 拦截js请求

return YES;

}

return NO;

}

// 收到请求返回data的代理方法

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

if([data verifySHA256Success]) {

[self.client URLProtocol:self didLoadData:data];

} else {

localData = [CacheManager bundleCacheFromUrl:url];

[self.client URLProtocol:self didLoadData:localData];

}

}

 

这里不用担心不能更新js文件,因为当后台的 js 文件有更新时,新 js 文件的签名就会发生变化,js 文件的URL也就自然变化,于是本地请求的时候,缓存是无法命中的,所以,也就会直接走下载 js 的那个路径。

缺点如下:
(1).在发生 js 劫持的时候,只能使用本地 js,可能会比最新版本 js 落后
(2).js 文件必须是由自己的服务端提供,并控制,才好对 js 进行签名,所以适用范围略窄
作为这个方案的扩充,可以考虑再次利用NSURLProtocol,当发现 js 被污染,重定向URL,此URL由服务端返回一个加密的 js 文件,对称加密,密钥插入在 js 的密文中,本地解密 js 文件,就可以保证得到最新的,安全的 js 文件了。

总结

本文总结的是HTTP的防劫持,其实现在苹果也建议我们使用HTTPS,是啊,如果大家都使用了HTTPS哪还有这么多事呢,但是苹果框架下的URL Loading System需要我们研究的东西还是比较多的,长路漫漫,Fighting~

你可能感兴趣的:(HTTP防劫持方案)