DNS防劫持

DNS劫持指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应。

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

DNS劫持的主要表现为看视频,点击之后莫名其妙的跳到了某些广告网站。正常情况下,当我们点击某个链接的时候,会向一个称作DNS服务器的东西发出请求,把链接转换成机器能够识别的ip地址,其过程如下:

域名->ip地址的过程被称作DNS解析。在这个过程中,由于DNS请求报文是明文状态,可能会在请求过程中被监测,然后攻击者伪装DNS服务器向主机发送带有假ip地址的响应报文,从而使得主机访问到假的服务器。

NSURLProtocol

NSURLProtocol是苹果提供给开发者的黑魔法之一,大部分的网络请求都能被它拦截并且篡改,以此来改变URL的加载行为。这使得我们不必改动网络请求的业务代码,也能在需要的时候改变请求的细节。作为一个抽象类,我们必须继承自NSURLProtocol才能实现中间攻击的功能。

  • 是否要处理对应的请求。由于网页存在动态链接的可能性,简单的返回YES可能会创建大量的NSURLProtocol对象,因此我们需要保证每个请求能且仅能被返回一次YES

      + (BOOL)canInitWithRequest: (NSURLRequest *)request;
      + (BOOL)canInitWithTask: (NSURLSessionTask *)task;
  • 是否要对请求进行重定向,或者修改请求头、域名等关键信息。返回一个新的NSURLRequest对象来定制业务
    
    
    
      + (NSURLRequest *)canonicalRequestForRequest: (NSURLRequest *)request;
    
    
  • 如果处理请求返回了YES,那么下面两个回调对应请求开始和结束阶段。在这里可以标记请求对象已经被处理过
    
    
    
      - (void)startLoading;
      - (void)stopLoading;
    
    

当发起网络请求的时候,系统会像注册过的NSURLProtocol发起询问,判断是否需要处理修改该请求,通过一下代码来注册你的子类



    [NSURLProtocol registerClass: [CustomURLProtocol class]];

DNS解析

一般情况下,考虑DNS劫持大多发生在使用webView的时候。相较于使用网页,正常的网络请求即便被劫持了无非是返回错误的数据、或者干脆404,而且对付劫持,普通请求还有其他方案选择,所以本文讨论的是如何处理网页加载的劫持。

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



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秒查询:



+ (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方法隐藏对ipv4ipv6地址的处理细节:



+ (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改成另外一个函数:



phost = gethostbyname2(host, AF_INET6);

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



+ (NSString *)getIpAddressFromHostName: (NSString *)host {
    NSString * ipAddress = [self getIpv6AddressFromHost: host];
    if (ipAddress == nil) {
        ipAddress = [self getIpv4AddressFromHost: host];
    }
    return ipAddress;
}

扩展

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

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

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

WebKit

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



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()]);
}

NSURLSession

AFNetworking替换成NSURLSession实现之后,常规的NSURLProtocol已经不能拦截请求了。为了能继续实现拦截功能,需要在NSURLSessionConfiguration中设置对拦截类的支持:



NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.protocolClasses = @[LXDDNSInterceptor class];

由于AFNetworkingSDWebImage都是采用默认的defaultSessionConfiguration初始化请求会话对象的,因此直接hook掉这个默认方法可以实现拦截适配:



+ (NSURLSessionConfiguration *)lxd_defaultSessionConfiguration {
    NSURLSessionConfiguration * configuration = [self lxd_defaultSessionConfiguration];
    configuration.protocolClasses = @[LXDDNSInterceptor class];
    return configuration;
}

但是为了避免省字数出现[NSURLSessionConfiguration new]的创建方式,hook上面的方法并不能保证能够拦截到请求。于是我把hook的目标放到了NSURLSession上,发现存在一个类方法构造器生成实例:



+ (NSURLSession *)sessionWithConfiguration: (NSURLSessionConfiguration *)configuration delegate: (id<NSURLSessionDelegate>)delegate delegateQueue: (NSOperationQueue *)queue;

最开始是想hook这个类方法,然而在class_getClassMethod获取所有的方法列表输出之后发现竟然不存在这个类方法,取而代之的是一个init构造器:

不知道这是不是苹果有意为之来误导开发者(苹果:我是爸爸,规则我来定)。但是通过代码联想又无法直接输出这个函数,于是通过category的方式暴露这个方法名,并且hook掉:



/// h文件
@interface NSURLSession (LXDIntercept)

- (instancetype)initWithConfiguration: (NSURLSessionConfiguration *)configuration delegate: (id<NSURLSessionDelegate>)delegate delegateQueue: (NSOperationQueue *)queue;

@end

/// m文件
@implementation NSURLSession (LXDIntercept)

+ (void)load {
    Method origin = class_getClassMethod([NSURLSession class], @selector(initWithConfiguration:delegate:delegateQueue:));
    Method custom = class_getClassMethod([NSURLSession class], @selector(lxd_initWithConfiguration:delegate:delegateQueue:));
    method_exchangeImplementations(origin, custom);
}

- (NSURLSession *)lxd_initWithConfiguration: (NSURLSessionConfiguration *)configuration delegate: (id<NSURLSessionDelegate>)delegate delegateQueue: (NSOperationQueue *)queue {
    if (lxd_url_session_configure) {
        lxd_url_session_configure(configuration);
    }
    return [self lxd_initWithConfiguration: configuration delegate: delegate delegateQueue: queue];
}

@end

于是,又能愉快的在项目里面玩耍网络拦截啦。

原文地址:https://www.cnblogs.com/asd667/p/9915256.html

时间: 2024-10-10 06:46:22

DNS防劫持的相关文章

linux的dns被劫持

环境:ubuntu16.04 今天遇到dns被劫持的情况,在此记录一下: 1.首先如何确定是否被劫持: 那么查询一个并不存在的域名 nslookup notexit.comrrrr 如果返回了一个ip地址,说明dns被劫持了,假设此ip地址为:123.34.5.6 那么用8.8.8.8域名服务器解析一下此错误域名试试: nslookup notexit.comrrrr 8.8.8.8 输出的内容如下: [email protected]:~$ nslookup notexit.comrrrr 8

全面了解移动端DNS域名劫持等杂症:原理、根源、HttpDNS解决方案等

1.引言 对于互联网,域名是访问的第一跳,而这一跳很多时候会"失足"(尤其是移动端网络),导致访问错误内容.失败连接等,让用户在互联网上畅游的爽快瞬间消失. 而对于这关键的第一跳,包括鹅厂在内的国内互联网大厂,都在持续深入地研究和思考对策,本文将就鹅厂团队在这一块的技术实践,做一个深度的总结和技术分享,希望给大家带来些许启发. 学习交流: - 即时通讯/推送技术开发交流4群:101279154[推荐] - 移动端IM开发入门文章:<新手入门一篇就够:从零开发移动端IM> (

XSS,CSRF,Cookie防劫持的处理

Cookie与sessionHTTP天然是无状态的协议, 为了维持和跟踪用户的状态, 引入了Cookie和Session. Cookie包含了浏览器客户端的用户凭证, 相对较小. Session则维护在服务器, 用于维护相对较大的用户信息. 用通俗的语言, Cookie是钥匙, Session是锁芯. Cookie简单理解就是钥匙, 每次去服务端获取资源, 需要带着这把钥匙, 只有自己的锁芯(资源), 才能打开.. 但是如果钥匙被别人拿了, 那别人就可以冒充你的身份, 去打开你的锁芯, 从而获取

全球免费公共 DNS 解析服务器 IP 地址列表推荐 (解决无法上网/加速/防劫持)

除了宽带提供商的 DNS 以外,像阿里云.腾讯云.Google.百度.IBM.CNNIC 等一些有足够实力的大型互联网公司也非常慷慨地为公众提供了免费的 DNS 解析服务器.异次元这就搜集了全球范围内这些可靠.免费高速的公共 DNS 服务器地址列表,供大家选择使用.(推荐使用 DNS Jumper 快速修改 DNS) 国内免费公共 DNS 域名解析服务收集 以下收集的都是目前全球范围内 (更多数是国内的) 比较靠谱的免费公共 DNS 服务的 IP 地址.由于地区差异,同一组 DNS 服务器在不同

精准调度,高速体验:移动防劫持D+详解

被誉为"互联网女皇"的KPCB合伙人玛丽·米克(Mary Meeker)在发布的年度互联网报告中指出,目前全球网民总数约为28亿人,较2014年增加8%.目前全球智能手机用户为21亿人,较2014年增长23%.中国手机网民规模5.27亿,手机网民占整体网民比例为83.4% D+就是DNSPOD研发的移动解析服务的专用名称.使用HTTP协议向D+服务器的80端口进行请求,代替传统的DNS协议向DNS服务器的53端口进行请求,绕开了运营商的Local DNS,从而避免了使用运营商Local

DNS劫持是什么意思?DNS被劫持怎么办?

域名是否被墙 被改标题 DNS污染检测 被挂黑链 网站是否被黑 网站打开速度检测 检测网站是否被劫持 被入侵 IIS7 网站监控 DNS劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能反应或访问的是假网址. 一.基本原理 DNS(域名系统)的作用是把网络地址(域名,以一个字符串的形式)对应到真实的计算机能够识别的网络地址(IP地址),以便计算机能够进一步通信,传递网址

dns被劫持怎么快速解决?

可以手动指定dns,来达到避免劫持的情况.也可以使用IIS7网站监控,通过输入自己的域名,来检测自己网站的实时安全情况,不管是检测问题还是确定问题,都需要这样做. 操作步骤: 一.打开控制面板,找到网络和intermet并打开. 二.打开页面打开网络和共享中心. 三.在当前页面找到本地连接,双击打开. 四.打开本地连接后点击属性栏,在属性页面找到协议版本4 并点击属性.五.在协议版本4属性页面选择"使用下面的DNS服务器地址",首选DNS服务器填写:114.114.114.114:备用

DNS 域名劫持、域名污染

1. DNS 的基本原理 DNS Domain name system 域名系统:互联网通信的基石是IP,但IP很多,也不好记忆,所以我们用域名来代替IP 来让人更容易记忆:那真正通信必须用IP来进行,所以需要把域名解析成IP. DNS 域名服务是一个树状结构,从一级域名com/net/cn/... 到多级域名,一个域名从自己近的域名进行解析,最终解析到IP 负责搭建.维护域名服务器的是运营商,运营商有电信.移动.联通.铁通.网通.教育网等:尤其是一些小运行商的域名服务器不是很稳定,有时会出现域

什么是DNS域名劫持、域名污染

1. DNS 的基本原理 DNS Domain name system 域名系统:互联网通信的基石是IP,但IP很多,也不好记忆,所以我们用域名来代替IP 来让人更容易记忆:那真正通信必须用IP来进行,所以需要把域名解析成IP. IIS7网站监控 测网站是否被劫持.域名是否被墙.DNS污染检测等信息. DNS 域名服务是一个树状结构,从一级域名com/net/cn/... 到多级域名,一个域名从自己近的域名进行解析,最终解析到IP 负责搭建.维护域名服务器的是运营商,尤其是一些小运行商的域名服务