移动端H5防劫持(防止广告注入)

移动端H5防劫持(防止广告注入)

最近项目中自己的H5网页出现了被劫持插入广告的事件,看好趁着这个节点整理下H5被劫持的原因及防止劫持的方法。

原因:

经过查找和调研市面上出现这种情况的原因大概分为三种:

1.DNS劫持(也就是运营商搞的鬼)

2.http劫持(此类情况最多)

3.项目中使用第三方的jar包

首先定位我们自己出现此类问题应该从以下几个方面查找、调试:

1.网络用的是4g还是wifi?

2.如果是wifi,是不是路边免费的wifi?

3.在什么页面出现?(方便定位是h5页面还是原生界面,对大部分用户,千万不要问他是h5还是原生,只能自己分析,如果原生界面也出现那么第三方jar出现问题的概率很大,如果只是h5界面,那么劫持的可能性很大)

4.使用的是android还是ios客户端

什么是DNS劫持:

首先DNS是什么。在因特网中,机器相互识别靠的是ip,而ip单纯的无意义数字的结合,很难被人类熟记,所以产生了域名,例如www.baidu.com 就是域名。那么问题来了,我们输入域名,机器又不认识,那么机器怎么去访问?那么就轮到DNS出场了,DNS在作为域名和IP地址相互映射的一个分布式数据库,就是我们的浏览器,会将域名拿到DNS去解析出ip地址来访问,DNS劫持是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能反应或访问的是假网址。
通俗来说,就是他给我们指向了另一个地址,或者让我们无法访问。电信以前的互联星空的,每次联网打开的第一个网页永远是互联星空。就是典型的DNS劫持。

什么是http劫持:

百度百科的说法:HTTP劫持是在使用者与其目的网络服务所建立的专用数据通道中,监视特定数据信息,提示当满足设定的条件时,就会在正常的数据流中插入精心设计的网络数据报文,目的是让用户端程序解释“错误”的数据,并以弹出新窗口的形式在使用者界面展示宣传性广告或者直接显示某网站的内容。
通俗来说,你要去百度的首页,他会给你百度首页,然后再百度首页的某个部位+个广告。 更具欺骗性,危害更大。

解决方案:

使用https请求替换http请求,可以有效的防止劫持。原理是:因为SSl协议唉http请求开始前增加了握手阶段:

https.png

在SSL握手阶段,客户端浏览器会认证服务器的身份,这是通过“证书”来实现的,证书由证书权威(CA)为某个域名签发,可以理解为网站的身份证件,客户端需要对这个证件进行认证,需要确定该证书是否属于目标网站并确认证书本身是否有效。最后在握手阶段,通信的双方还会协商出一个用于加密和解密的会话密钥。

SSL握手阶段结束之后,服务器和客户端使用协商出的会话密钥对交互的数据进行加密/解密操作,对于HTTP协议来说,就是将HTTP请求和应答经过加密之后再发送到网络上。

移动端处理:

1.移动端拦截协议、只拦截自己的加载本地的js相关。NSURLProtocol->、SystemWebViewClient -> shouldInterceptRequest

2.可以创建黑名单、服务端下发、实时动态更新黑名单、手机端代码拦截名单中的域名或者请求

android代码处理:

webView.setWebViewClient(new WebViewClient() { // Load opened URL in the application instead of standard browser // application
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            showLogInfo("拦截到的url----"+url);
            String advertising="http://"+sharedPreferencesUtil.getData(Constant.IP, RequestConfig.IP) +":"+sharedPreferencesUtil.getData(Constant.PORT,RequestConfig.IPPORT);
            if (url.contains(pre)) {
                Map<String, String> map = getParamsMap(url, pre);
                String code = map.get("code");
                String data = map.get("data");
                parseCode(code, data);
                return true;
    
            }else if(!url.contains(advertising)){
                showLogError("拦截到植入广告,广告的url——"+url);
                return true;
    
            } else{
                return false;
    
            }
        }
    });
 

iOS处理代码:

1.在WebView代理里拦截

2.全局拦截请求


#import "ZMURLProtocol.h"
static NSString * const URLProtocolHandledKey = @"URLProtocolHandledKey";
static NSDictionary *_holdUpDic;
@interface ZMURLProtocol ()<NSURLConnectionDelegate>
@property (nonatomic, strong) NSURLConnection *connection;
@end
@implementation ZMURLProtocol
+(NSDictionary *)getHoldUpDic {
    if (!_holdUpDic) {
#pragma mark - 这里是获取黑白名单的数据 /*
        [AFNetWork postWithURL:@"" Params:@"" Success:^(NSURLSessionDataTask *task, id responseObject) {
            //获取广告拦截资料
            _holdUpDic = responseObject;
            //写入本地plist文件
            BOOL success = [_holdUpDic writeToFile:path atomically:YES];
            if (success ) {
                NSLog(@"写入成功");

            }else {
                NSLog(@"写入失败"); } }];
        _holdUpDic = [NSDictionary dictionaryWithContentsOfFile:path]; */ }
    return _holdUpDic;
    
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    //只处理http和https请求
    NSString *scheme = [[request URL] scheme];
    if ( ([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame )||([scheme caseInsensitiveCompare:@"https"] == NSOrderedSame )) {
        //看看是否已经处理过了,防止无限循环
        if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
            return NO;
            
        }
        return YES;//处理
        
    }
    return NO;
    
}
+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
    //网页发生变动
     [[NSNotificationCenter defaultCenter] postNotificationName:PageChangeNotification object:self userInfo:nil];
    //
    NSLog(@"canonicalRequestForRequest:%@",request.URL.absoluteString);
    NSMutableURLRequest *mutableReqeust = [request mutableCopy];
    mutableReqeust = [self redirectHostInRequset:mutableReqeust]; return mutableReqeust;
    
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
    return [super requestIsCacheEquivalent:a toRequest:b];
    
}
- (void)startLoading {
    NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
    //打标签,防止无限循环
    [NSURLProtocol setProperty:@YES forKey:URLProtocolHandledKey inRequest:mutableReqeust];
    //
    self.connection = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self]; }
    //
- (void)stopLoading {
    [self.connection cancel];
    
}
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self.client URLProtocol:self didFailWithError:error];
    
}
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response {
    // if (response != nil)
    // {
    //[[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];
    // }
    //这里需要回传[self client] 消息,那么需要重定向的网页就会出现问题:host不对或者造成跨域调用导致资源无法加载
    [[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];
    // return request;
    // 这里如果返回 request 会重新请求一次
    return nil;
    
}
 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection {
     return YES;
     
 }
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { [self.client URLProtocol:self didReceiveAuthenticationChallenge:challenge];
    
}
- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    [self.client URLProtocol:self didCancelAuthenticationChallenge:challenge];
    
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
    
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
    
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
    return cachedResponse;
    
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [self.client URLProtocolDidFinishLoading:self];
    
}
#pragma mark -- private
+(NSMutableURLRequest*)redirectHostInRequset:(NSMutableURLRequest*)request {
    //没有域名的URL请求就原路返回,不能返回nil ,不然在跳转APP的时候会被拦截返回空出错(或者其他情况). //eg: mqq://im/chat?chat_type=wpa&uin=1299101858&version=1&src_type=web 跳转到指定QQ用户的聊天窗口
    if ([request.URL host].length == 0) {
        return request;
        
    }
    NSString *originUrlString = request.URL.absoluteString;
    //获取主机名字,在这里执行正则匹配
    NSString *originHostString = [request.URL host]; NSRange hostRange = [originUrlString rangeOfString:originHostString];
    //找不到主机名,返回
    if (hostRange.location == NSNotFound) {
        return request;
        
    }
    if (originUrlString != nil) {
        //获取拦截的黑白名单数据(过滤名单)
        //这个是自定义方法,你们自己随意发挥,哈哈哈.
        #warning --- 思路实现
        /* 这里的匹配黑白名单一般只是**匹配域名** 思路 1:匹配白名单->匹配黑名单-> 如果两个都没有,就向服务器打印日志. (拉外网) 思路 2:匹配白名单 以下代码运用思路1 实现 eg: 这个是过滤的规则的例子格式 .*(.qq.com|api.weibo.com|.weibo.com|.baidu.com|.weixin.qq.com|.sina.com|.sina.cn).* */
        NSDictionary *dic = [self getHoldUpDic];
        if (!dic)
            //如果为空不处理黑白名单
        {
            return request;
            
        }
        //白名单
        NSString *whiteList = dic[@"whiteList"];
        //黑名单
        NSString * blackList = dic[@"blackList"];
        #pragma mark - 白名单匹配
        //1.1将正则表达式设置为OC规则
        if (![whiteList isEqualToString:@""]) { NSRegularExpression *regular1 = [[NSRegularExpression alloc] initWithPattern:whiteList options:NSRegularExpressionCaseInsensitive error:nil];
            //2.利用规则测试字符串获取匹配结果
            NSArray *results1 = [regular1 matchesInString:originUrlString options:0 range:NSMakeRange(0, originUrlString.length)];
            if (results1.count > 0)
                //是白名单,允许访问
            {
                return request;
                
            }
            
        }
        #pragma mark - 黑名单匹配
        if (![blackList isEqualToString:@""]) {
            //1.1将正则表达式设置为OC规则
            NSRegularExpression *regular2 = [[NSRegularExpression alloc] initWithPattern:blackList options:NSRegularExpressionCaseInsensitive error:nil];
            //2.利用规则匹配字符串获取匹配结果
            NSArray *results2 = [regular2 matchesInString:originUrlString options:0 range:NSMakeRange(0, originUrlString.length)];
            if (results2.count > 0 )
                //黑名单,返回nil;
            { return request;
                
            }
            
        }
        if (![whiteList isEqualToString:@""]&&![blackList isEqualToString:@""]) {
        #pragma mark - 发送到服务端打印日志 
            
        }
        
    }
    return request;
    
}
@end




H5处理:

嵌入的代码基本都是iframe,把以下js代码加入 body标签内,以删除iframe(记得用script标签包裹)

//以下代码为删除嵌入广告
var del_times = 0, deTimer = null;
function adGo() {
    var iframe = document.getElementsByTagName('iframe')[0];
    if(iframe){
        console.log(iframe)
        
        //循环 iframe 父类,直到找到body和body的下一级,然后整个嵌入的代码删除。
        var bodyNode = {tagName:''}, iframeParent, targetNode = iframe.parentNode;
        while (bodyNode.tagName != 'BODY'){
            bodyNode = targetNode;
            if(bodyNode.tagName != 'BODY'){
                iframeParent = targetNode;
                targetNode = targetNode.parentNode;
            }
        }
        if(iframeParent) //如果iframe有父类
            bodyNode.removeChild(iframeParent);
        else
            bodyNode.removeChild(iframe);
    }
    del_times++;
    if (del_times > 10) window.clearInterval(deTimer)
}
deTimer = self.setInterval(adGo, 1000);   //把这个1000, 调低一点,比如200

以上就是处理方法,如有遗漏请大家指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容