DNS劫持的基本原理与解决方案方案

 检测网站是否被劫持
 域名是否被墙
 DNS污染检测
 网站打开速度检测
 网站是否被黑
 被入侵
 被改标题
 被挂黑链 网站监控 

DNS劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能反应或访问的是假网址。

基本原理

  DNS(域名系统)的作用是把网络地址(域名,以一个字符串的形式)对应到真实的计算机能够识别的网络地址(IP地址),以便计算机能够进一步通信,传递网址和内容等。由于域名劫持往往只能在特定的被劫持的网络范围内进行,所以在此范围外的域名服务器(DNS)能够返回正常的IP地址,高级用户可以在网络设置把DNS指向这些正常的域名服务器以实现对网址的正常访问。所以域名劫持通常相伴的措施——封锁正常DNS的IP。如果知道该域名的真实IP地址,则可以直接用此IP代替域名后进行访问。比如访问百度域名,可以把访问改为202.108.22.5,从而绕开域名劫持
如下图所示

Alt text

NSURLProtocol

官方文档这样描述

1
An NSURLProtocol object handles the loading of protocol-specific URL data. The NSURLProtocol class itself is an abstract class that provides the infrastructure for processing URLs with a specific URL scheme. You create subclasses for any custom protocols or URL schemes that your app supports.

 

NSURLProtocol也是苹果众多黑魔法中的一种,使用它可以轻松地重定义整个URL Loading System。当你注册自定义NSURLProtocol后,就有机会对所有的请求进行统一的处理,基于这一点它可以让你

(1).自定义请求和响应

(2).提供自定义的全局缓存支持

(3).重定向网络请求

(4).提供HTTP Mocking (方便前期测试)

(5).其他一些全局的网络请求修改需求

这使得我们不必改动网络请求的业务代码,也能在需要的时候改变请求的细节。作为一个抽象类,我们必须继承自NSURLProtocol才能实现中间攻击的功能。

使用方法
(1).继承NSURLPorotocl,并注册你的NSURLProtocol
[NSURLProtocol registerClass:[XXURLProtocol class]];

当NSURLConnection准备发起请求时,它会遍历所有已注册的NSURLProtocol,询问它们能否处理当前请求。所以你需要尽早注册这个Protocol。

(2).是否要处理对应的请求
当遍历到我们自定义的NSURLProtocol时,系统先会调用canInitWithRequest:这个方法。顾名思义,这是整个流程的入口,只有这个方法返回YES我们才能够继续后续的处理。由于网页存在动态链接的可能性,简单的返回YES可能会创建大量的NSURLProtocol对象,因此我们需要保证每个请求能且仅能被返回一次YES。

1
2
+ (BOOL)canInitWithRequest: (NSURLRequest *)request;
+ (BOOL)canInitWithTask: (NSURLSessionTask *)task;

 

(3).是否要对请求进行重定向,或者修改请求头、域名等关键信息。
当遍历到我们自定义的NSURLProtocol时,系统先会调用canInitWithRequest:这个方法。顾名思义,这是整个流程的入口,只有这个方法返回YES我们才能够继续后续的处理。由于网页存在动态链接的可能性,简单的返回YES可能会创建大量的NSURLProtocol对象,因此我们需要保证每个请求能且仅能被返回一次YES。

1
+ (NSURLRequest *)canonicalRequestForRequest: (NSURLRequest *)request;

 

(4).标记是否被处理过
当遍历到我们自定义的NSURLProtocol时,系统先会调用canInitWithRequest:这个方法。顾名思义,这是整个流程的入口,只有这个方法返回YES我们才能够继续后续的处理。由于网页存在动态链接的可能性,简单的返回YES可能会创建大量的NSURLProtocol对象,因此我们需要保证每个请求能且仅能被返回一次YES。

1
2
- (void)startLoading;
- (void)stopLoading;

 

DNS劫持

对于iOS开发来讲,DNS劫持一般发生在使用webView的时候,我们更多地是处理网页加载的劫持。

LocalDNS是一种常见的防劫持方案。简单来说,在网页发起请求的时候获取请求域名,然后在本地进行解析得到ip,返回一个直接访问网页ip地址的请求。结构体struct hostent用来表示地址信息:

1
2
3
4
5
6
7
struct hostent {
char *h_name; // official name of host
char **h_aliases; // alias list
int h_addrtype; // host address type——AF_INET || AF_INET6
int h_length; // length of address
char **h_addr_list; // list of addresses
};

 

C函数gethostbyname使用递归查询的方式将传入的域名转换成struct hostent结构体,但是这个函数存在一个缺陷:由于采用递归方式查询域名,常常会发生超时。但是gethostbyname本身不支持超时处理,所以这个函数调用的时候放到操作队列中执行,并且采用信号量等待1.5秒查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
+ (struct hostent *)getHostByName: (const char *)hostName {
__block struct hostent * phost = NULL;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSOperationQueue * queue = [NSOperationQueue new];
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock: ^{
phost = gethostbyname(hostName);
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC));
[queue cancelAllOperations];
return phost;
};

然后通过函数inet_ntop把结构体中的地址信息符号化,获得C字符串类型的地址信息。提供getIpAddressFromHostName方法隐藏对ipv4和ipv6地址的处理细节:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
+ (NSString *)getIpv4AddressFromHost: (NSString *)host {
const char * hostName = host.UTF8String;
struct hostent * phost = [self getHostByName: hostName];
if ( phost == NULL ) { return nil; }
 
struct in_addr ip_addr;
memcpy(&ip_addr, phost->h_addr_list[0], 4);
 
char ip[20] = { 0 };
inet_ntop(AF_INET, &ip_addr, ip, sizeof(ip));
return [NSString stringWithUTF8String: ip];
}
 
+ (NSString *)getIpv6AddressFromHost: (NSString *)host {
const char * hostName = host.UTF8String;
struct hostent * phost = [self getHostByName: hostName];
if ( phost == NULL ) { return nil; }
 
char ip[32] = { 0 };
char ** aliases;
switch (phost->h_addrtype) {
case AF_INET:
case AF_INET6: {
for (aliases = phost->h_addr_list; *aliases != NULL; aliases++) {
NSString * ipAddress = [NSString stringWithUTF8String: inet_ntop(phost->h_addrtype, *aliases, ip, sizeof(ip))];
if (ipAddress) { return ipAddress; }
}
} break;
 
default:
break;
}
return nil;
}
 
+ (NSString *)getIpAddressFromHostName: (NSString *)host {
NSString * ipAddress = [self getIpv4AddressFromHost: host];
if (ipAddress == nil) {
ipAddress = [self getIpv6AddressFromHost: host];
}
return ipAddress;
}

适配IPv6

苹果明确现在的的应用要支持IPv6地址,对于开发者来说,并没有太大的改动,无非是将gethostbyname改成另外一个函数:

1
phost = gethostbyname2(host, AF_INET6);

 

另外就是解析域名过程中优先获取IPv6的地址而不是IPv4:

1
2
3
4
5
6
7
+ (NSString *)getIpAddressFromHostName: (NSString *)host {
NSString * ipAddress = [self getIpv6AddressFromHost: host];
if (ipAddress == nil) {
ipAddress = [self getIpv4AddressFromHost: host];
}
return ipAddress;
}

 

服务器下发对应的DNS解析列表代替递归查询的方法

localDNS直接进行解析获取的ip地址可能不是最优选择,另一种做法是让应用每次启动后从服务器下发对应的DNS解析列表,直接从列表中获取ip地址访问。这种做法对比递归式的查询,无疑效率要更高一些,需要注意的是在下发请求过程中如何避免解析列表被中间人篡改。

因为请求地址可能无效,需要以ip映射host的映射表来保证在访问无效的地址之后能重新使用原来的域名发起请求。另外确定ip无效后应该维护一个无效地址表,用来域名解析后判断是否继续使用地址访问。

此外,如果你的应用还没有服务器下发DNS解析列表这一业务,那么直接使用Local DNS解析可能会遇到解析出来的ip无效问题。目前上面代码的处理是如果ip无效,发起回调让webView重新加载。除此之外有另外一种解决方案。应用本地存储一张需要访问到的域名表,然后在程序启动之后异步执行域名解析过程,参照DNS解析失败的处理 (支持IPv6)一文,提前做好无效解析的处理。

针对WebKit的解决方案

WKWebView是苹果推出的UIWebView的替代方案,但前者还不够优秀以至于使用后者开发的大有人在。另外使用NSURLProtocol实现防DNS劫持功能的时候,在调起canInitWithRequest:后就再无下文,不过呢通过查阅资料发现想实现WebKit的请求拦截需要调用一些私有方法,让WKWebView 支持 NSURLProtocol 文章已经做了很好的处理,在文中的基础上,可以对注册协议的过程多加了一层处理。远程桌面连接软件

1
2
3
4
5
6
7
8
9
10
11
12
13
static inline NSString * lxd_scheme_selector_suffix() {
return @"SchemeForCustomProtocol:";
}
 
static inline SEL lxd_register_scheme_selector() {
const NSString * const registerPrefix = @"register";
return NSSelectorFromString([registerPrefix stringByAppendingString: lxd_scheme_selector_suffix()]);
}
 
static inline SEL lxd_unregister_scheme_selector() {
const NSString * const unregisterPrefix = @"unregister";
return NSSelectorFromString([unregisterPrefix stringByAppendingString: lxd_scheme_selector_suffix()]);
}

 

猜你喜欢

转载自www.cnblogs.com/asd667/p/9900484.html