一种通过HTTPDNS解决域名劫持问题的技术方案介绍

举报
JohnTai 发表于 2018/12/27 16:51:31 2018/12/27
【摘要】 之前有用户反映,不管通过通过PC端、还是移动端访问我们的服务都会不定时出现域名劫持的问题。为了解决这个问题,我们只能绕过传统的运营商域名解析,通过IP直接访问服务。本文对集成HttpDNS作简要介绍。一、HTTPDNS介绍:httpDNS提供了面向移动端的SDK,移动端可以通过传入域名的方式调用,SDK会直接返回解析出的IP地址。

之前有用户反映,不管通过通过PC端、还是移动端访问我们的服务都会不定时出现域名劫持的问题。为了解决这个问题,我们只能绕过传统的运营商域名解析,通过IP直接访问服务。本文对集成HttpDNS作简要介绍。

一、HTTPDNS介绍:

httpDNS提供了面向移动端的SDK,移动端可以通过传入域名的方式调用,SDK会直接返回解析出的IP地址。

NSString *ip = [[HttpDnsService sharedInstance] getIpByHostAsync:Domain];

二、需求描述:

对项目中: 1.原生图片的请求, 2.H5网页中请求,分别做IP直连处理。

三、实现方案:

  • 实现原理:

通过注册NSURLProtocol,拦截所有请求,过滤出相应的图片请求及H5网页请求,将请求的url中的域名替换为IP后,重新发起请求,获取到响应数据后,回调给URL Loading System。

  • 实现过程:

1.拦截请求

由于原生图片和H5网页中的请求需要分开处理以便于实现通过降级开关分别控制,所以注册了两个NSURLProtocol分别处理这两项业务,具体策略为: YH_ImageProtocol在拦截到请求后,按照URL后缀(是否包含:.jpg/.jpeg/.png/.gif)过滤出图片的URL。 YH_WebProtocol在拦截到请求后,排除图片的URL,则认为是需要拦截的请求。

2. 手动发起请求

拦截到请求后,需要根据协议分别做处理:如果是HTTP请求,使用NSURLSession重新发起请求,获取到响应的数据后,回调给URL Loaidng System。如果是HTTPS请求,由于当前请求URL的域名被替换成了IP地址,请求URL中的host也会被替换成HTTPDNS解析出来的IP,导致服务器获取到的域名为解析后的IP,无法找到匹配的证书,只能返回默认的证书或者不返回,所以会出现SSL/TLS握手不成功的错误。为了解决这个问题,我们需要hook HTTPS访问前SSL连接过程,根据网络请求头部域中的HOST信息,设置SSL连接PeerHost的值,之后根据服务器返回的证书执行验证过程。所以在拦截网络请求后,使用CFHTTPMessageRef创建NSInputStream实例进行Socket通信,并设置其kCFStreamSSLPeerName的值:

// 创建CFHTTPMessage对象的输入流
CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault,cfrequest);
inputStream = (__bridge_transfer NSInputStream *) readStream;
    
// 设置SNI host信息
NSString *host = [curRequest.allHTTPHeaderFields objectForKey:@"host"];
    if (!host) {
        host = curRequest.URL.host;
    }
    [inputStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL forKey:NSStreamSocketSecurityLevelKey];
    NSDictionary *sslProperties = [[NSDictionary alloc] initWithObjectsAndKeys:
                                   host, (__bridge id) kCFStreamSSLPeerName,
                                   nil];
    [inputStream setProperty:sslProperties forKey:(__bridge_transfer NSString *) kCFStreamPropertySSLSettings];
    [inputStream setDelegate:self];
3.重定向

当返回的StatusCode在300、400之间,且header中location字段中取出合法的URL时,用该URL初始化新的请求,在protocol内部重新执行一遍之前的流程。

四、碰到的问题及解决方法:

  • GZIP

之前在测试过程中发现,用Webview加载官网时,页面显示乱码。经排查,确认是返回的content-type为gzip,因为未解压导致页面无法识别。为此,我们在收到响应后,先判断content类型,如果为gzip,先进行解压再回调给相应的client。

  • CSS文件中通过相对路径的方式引用的静态资源无法加载

该问题发生的具体原因是:在WebView中的请求被拦截,域名改为IP直连后,CSS文件中通过相对路径引用的静态资源(包括iconfont和少量图片)的url直接沿用了CSS文件URL中的IP地址作为域名,跳过了域名解析的步骤,且header中的HOST字段未设置为相应的域名。最终导致无法通过SNI扩展的方式获取到SSL证书,建连失败。我们的解决方案是保存好IP地址和域名的映射关系,碰到前述问题时,能够获取到IP地址对应的域名,设置给HOST,以保证SSL握手成功。

五、待改进的地方:

目前的业务需求是,拦截到的H5请求,全部强制转为HTTPS方式请求。这种情况下会导致一些服务端不支持HTTPS的请求失败,尤其跳转到一些第三方网站的页面。为避免该问题,我们应该提供一种容错机制,当强制使用HTTPS的方式去打开页面时,如果SSL握手失败,可以再改为HTTP的方式去请求。


【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。