iOS DNS反劫持

一、DNS介绍

        DNS(Domain Name System,域名系统),万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。简单说就是网络地址IP由数字组成的,例如百度的服务器地址是115.239.210.27,浏览器可以通过这个地址访问百度,但是这个数字记忆起来比较麻烦,于是就有了www.baidu.com这个更直观的表示方法,DNS就是连接这两者的服务。

二、DNS解析

         域名到IP地址的映射,DNS解析请求采用UDP(User Datagram Protocol,用户数据报协议)数据报且明文的,现在假如我们访问一个网站www.baidu.com从按下回车到百度页面显示到我们的电脑上会经历如下几个步骤:

1:计算机会向我们的运营商(移动、电信、联通等)发出打开www.baidu.com的请求。

2:运营商收到请求后会到自己的DNS服务器中找www.baidu.com这个域名所对应的服务器的IP地址(也就是百度的服务器的IP地址),这里比如是115.239.210.27。

3:运营商用第二步得到的IP地址去找到百度的服务器请求得到数据后返回给我们,其中第二步就是我们所说的DNS解析过程,域名和IP地址的关系其实就是我们的身份证号和姓名的关系,都是来标记一个人或者是一个网站的,只是IP地址/身份证号只是一串没有意义的数字,辨识度低,又不好记,所以就会在IP上加上一个域名以便区分,或是做的更加个性化,但是如果真的要来准确的区分还是要靠身份证号码或者是IP的,所以DNS解析就应运而生了。

三、DNS劫持

DNS劫持,由于域名->IP这个过程中,其解析是基于UDP协议实现,所以报文是明文状态,可能会在请求过程中被监测,然后攻击者做一些自己的处理,比如返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能反应或访问的是假网址。根本原因就是以下两点:

1:恶意攻击,拦截运营商的解析过程,把自己的非法东西嵌入其中。

2:运营商为了利益或者一些其他的因素,允许一些第三方在自己的链接里打打广告之类的。

四、防止DNS劫持

        了解了DNS劫持的相关资料后我们就知道了,防止DNS劫持就要从第二步入手,因为DNS解析过程是运营商来操作的,我们不能去干涉他们,不然我们也就成了劫持者了,所以我们要做的就是在我们请求之前对我们的请求链接做一些修改,将我们原本的请求链接www.baidu.com修改为115.239.210.27,然后请求出去,这样的话就运营商在拿到我们的请求后发现我们直接用的就是IP地址就会直接给我们放行,而不会去走他自己DNS解析了,也就是说我们把运营商要做的事情自己先做好了。不走他的DNS解析也就不会存在DNS被劫持的问题,从根本是解决了。

        我们知道要要把项目中请求的接口替换成IP其实很简单,URL是字符串,域名替换IP,无非就是一个字符串替换而已,的确这块其实没有什么技术含量,这里要说一下IP地址的来源,如何拿到一个域名所对应的IP呢?这里就是需要用到另一个服务——HTTPDNS。

1. HTTPDNS

        HTTPDNS 是客户端基于http协议向服务器A发送域名B解析请求(例如:www.baidu.com),服务器A直接返回域名B对应的ip地址(例如:115.239.210.27),客户端获取到的IP后就向直接往此IP发送业务协议请求,这种方式替代了基于DNS协议向运营商LocalDNS发起解析请求,可以从根本上避免LocalDNS造成的域名劫持问题,常规的DNS解析是通过UDP方式。

       国内提供域名解析 API 接口的,有 DNSPod,现在国内有很多厂商为 DNSPod 开发了 SDK,比如 阿里、七牛(开源)等。不想自己写的,不妨使用这些 SDK。

2. 内置IP列表

       可以在启动等阶段由服务端下发域名和 IP 的对应列表,客户端来进行缓存,发起网络请求的时候直接根据缓存 IP 来进行业务访问实现 HTTP 协议下 IP 连接其实是很简单的,我们只需要通过 NSURLProtocol 来拦截网络请求,然后将符号条件的网络请求 URL 中的域名修改为 IP 就可以啦,也就是本地域名解析LocalDNS。

#include
#include

/**
 * 通过hostname获取ip列表 DNS解析地址
 */
- (NSArray *)getDNSsWithDormain:(NSString *)hostName{
    NSMutableArray *result = [[NSMutableArray alloc] init];
    NSArray *IPV4DNSs = [self getIPV4DNSWithHostName:hostName];
    if (IPV4DNSs && IPV4DNSs.count > 0) {
        [result addObjectsFromArray:IPV4DNSs];
    }
   
    //由于在IPV6环境下不能用IPV4的地址进行连接监测
    //所以只返回IPV6的服务器DNS地址
    NSArray *IPV6DNSs = [self getIPV6DNSWithHostName:hostName];
    if (IPV6DNSs && IPV6DNSs.count > 0) {
        [result removeAllObjects];
        [result addObjectsFromArray:IPV6DNSs];
    }
   
    return [NSArray arrayWithArray:result];
}

//ipv4
- (NSArray *)getIPV4DNSWithHostName:(NSString *)hostName
{
    const char *hostN = [hostName UTF8String];
    struct hostent *phost;
   
    @try {
        phost = gethostbyname(hostN);
    } @catch (NSException *exception) {
        return nil;
    }
   
    NSMutableArray *result = [[NSMutableArray alloc] init];
    int j = 0;
    while (phost && phost->h_addr_list && phost->h_addr_list[j]) {
        struct in_addr ip_addr;
        memcpy(&ip_addr, phost->h_addr_list[j], 4);
        char ip[20] = {0};
        inet_ntop(AF_INET, &ip_addr, ip, sizeof(ip));
       
        NSString *strIPAddress = [NSString stringWithUTF8String:ip];
        [result addObject:strIPAddress];
        j++;
    }
   
    return [NSArray arrayWithArray:result];
}

//ipv6
- (NSArray *)getIPV6DNSWithHostName:(NSString *)hostName
{
    const char *hostN = [hostName UTF8String];
    struct hostent *phost;
   
    @try {
        /**
         * 只有在IPV6的网络下才会有返回值
         */
        phost = gethostbyname2(hostN, AF_INET6);
    } @catch (NSException *exception) {
        return nil;
    }
   
    NSMutableArray *result = [[NSMutableArray alloc] init];
    int j = 0;
    while (phost && phost->h_addr_list && phost->h_addr_list[j]) {
        struct in6_addr ip6_addr;
        memcpy(&ip6_addr, phost->h_addr_list[j], sizeof(struct in6_addr));
        NSString *strIPAddress = [self formatIPV6Address: ip6_addr];
        [result addObject:strIPAddress];
        j++;
    }
   
    return [NSArray arrayWithArray:result];
}

- (NSString *)formatIPV6Address:(struct in6_addr)ipv6Addr{
    NSString *address = nil;
   
    char dstStr[INET6_ADDRSTRLEN];
    char srcStr[INET6_ADDRSTRLEN];
    memcpy(srcStr, &ipv6Addr, sizeof(struct in6_addr));
    if(inet_ntop(AF_INET6, srcStr, dstStr, INET6_ADDRSTRLEN) != NULL){
        address = [NSString stringWithUTF8String:dstStr];
    }
   
    return address;
}

所以调用直接使用:[selfgetDNSsWithDormain:@"www.baidu.com"];

作者:小盟城主 链接:https://www.jianshu.com/p/b2bbb9afd3e9

你可能感兴趣的:(iOS DNS反劫持)