移动端如何做好数据防护

截至2022年底,移动互联网用户数接入数达14.6亿户,当年移动互联网接入流量达2617.6亿GB,比上年增长18.1%。流量的背后,是海量的数据,而数据又逐渐成为各个企业的核心资产。做好数据的防护工作,是每一个互联网企业需要认真对待的一件事情。

一、数据防护范围的界定

现在绝大多数的App都强依赖网络,客户端所展示的内容都是通过接口从服务器上获取,同时客户端也会向服务器上传数据,在两端通信的过程中,数据容易被第三方获取,导致数据泄漏或接口被盗刷。本文主要从防止数据被抓包的方向来分析在移动端如何做好数据防护。

二、抓包的核心原理

HTTPS抓包原理:Fiddler、Charles等抓包工具,其实都是采用了中间人攻击的方案: 将客户端的网络流量代理到MITM(中间人)主机,再通过一系列的面板或工具将网络请求结构化地呈现出来。

抓包HTTPS的两个突破点:CA证书校验是否合法;数据传递过程中的加密和解密。如果需要抓包,则需要MITM(中间人)伪造证书和使用自己的加解密方式。

抓包工作流程: 1.中间人截获客户端向发起的HTTPS请求,佯装客户端,向真实的服务器发起请求; 2.中间人截获真实服务器的返回,佯装真实服务器,向客户端发送数据; 3.中间人获取了用来加密服务器公钥的非对称秘钥和用来加密数据的对称秘钥,处理数据加解密。

  • 下图以Charles为例描述抓包原理

三、客户端通过关闭代理的方式防止抓包

3.1 检查手机是否打开了代理

(1)iOS实现方法

swift 复制代码
+ (BOOL)isProxy {    
    NSDictionary *proxySettings =  (__bridge NSDictionary *)(CFNetworkCopySystemProxySettings());    
    NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:@"http://www.baidu.com"]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));    
    NSDictionary *settings = [proxies objectAtIndex:0];        
    if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"]) {
        //没有设置代理        
        return NO;    
    }else{       
        //设置代理了        
        return YES;    
    }
}

(2)安卓实现方法

java 复制代码
private fun checkWifiProxy(): Boolean {
    val IS_ICS_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH        
    val proxyAddress: String?        
    val proxyPort: Int? 
    if (IS_ICS_OR_LATER) {            
        proxyAddress = System.getProperty("http.proxyHost")//获取代理主机
        val portStr = System.getProperty("http.proxyPort")//获取代理端口           
        proxyPort = Integer.parseInt(portStr ?: "-1")        
    } else {            
        proxyAddress = android.net.Proxy.getHost(this)            
        proxyPort = android.net.Proxy.getPort(this)       
    }        
    Log.i("cxmyDev","proxyAddress : ${proxyAddress}, prot : ${proxyPort}")
    return !TextUtils.isEmpty(proxyAddress) && proxyPort != -1    
}

3.2关闭代理

3.2.1 iOS客户端实现方法

(1)设置代理为空

swift 复制代码
// 禁止代理
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
configuration.connectionProxyDictionary = @{};

(2)不侵入代码的设置方式

swift 复制代码
@interface NSURLSession (Extensions)

+ (void)proxyEnabled:(BOOL)enabled;

@end

#import <objc/runtime.h>

@implementation NSURLSession (Extensions)

static BOOL proxyEnabled = YES;

+ (void)load {
    [super load];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [NSURLSession class];
        [self swizzleMethodWithClass:class
                    originalSelector:NSSelectorFromString(@"sessionWithConfiguration:")
                    swizzledSelector:NSSelectorFromString(@"_sessionWithConfiguration:")];
        [self swizzleMethodWithClass:class
                    originalSelector:NSSelectorFromString(@"sessionWithConfiguration:delegate:delegateQueue:")
                    swizzledSelector:NSSelectorFromString(@"_sessionWithConfiguration:delegate:delegateQueue:")];
    });
}

+ (void)proxyEnabled:(BOOL)enabled {
    proxyEnabled = enabled;
}

+ (NSURLSession *)_sessionWithConfiguration:(NSURLSessionConfiguration *)configuration {
    if (!proxyEnabled) {
        configuration.connectionProxyDictionary = @{};
    }
    return [self _sessionWithConfiguration:configuration];
}

+ (NSURLSession *)_sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
                                   delegate:(nullable id<NSURLSessionDelegate>)delegate
                              delegateQueue:(nullable NSOperationQueue *)queue {
    if (!proxyEnabled) {
        configuration.connectionProxyDictionary = @{};
    }
    return [self _sessionWithConfiguration:configuration delegate:delegate delegateQueue:queue];
}

+ (void)swizzleMethodWithClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector {
    Method originalMethod = class_getClassMethod(cls, originalSelector);
    Method swizzledMethod = class_getClassMethod(cls, swizzledSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

3.2.2 安卓客户端实现方法

(1)设置代理为空

java 复制代码
URL url1 = new URL("url");
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);

(2)在okhttp中设置禁止使用代理

java 复制代码
var httpBuilder = OkHttpClient.Builder().addInterceptor(defaultInterceptor()).connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS).writeTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS).readTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS).proxy(Proxy.NO_PROXY)

使用这种方式来防止抓包,优点是实现简单,无系统版本兼容问题;缺点是该方案比较粗暴,对于合理诉求需求使用网络代理的场景无法满足。且可以通过VPN导流进行抓包,即现将手机请求导到VPN,再对VPN的网络进行代理,绕过对App的代理。

四、其他方案

关闭代理的方式只是一种较为初步的防范手段,除此之外,还可以通过证书校验、双向认证等方式来避免App被恶意抓包。

4.1 证书校验(单向认证)

基于"中间人攻击"时会伪造证书,在通信前进行证书校验。证书校验即客户端下载服务端公钥证书,然后将证书保存到App本地,由应用在交互过程中验证证书的合法性。证书校验流程如下:

4.2 双向认证

单向认证安全性相对较高,证书锁定破解比较复杂,但通过一些工具,采用hook各网络框架的证书认证方法,替换原有逻辑,可以使校验失效。如果需要更高等级的保护手段,可以选择双向认证的方式。

双向认证是指除了客户端需要校验服务端的真实性,服务端也需要校验客户端的真实性。双向认证需要服务端支持,客户端必须内置一套公钥证书和私钥。在SSL/TLS握手过程中,服务端会向客户端请求证书,客户端必须将内置公钥发给服务端,服务端验证公钥证书的真实性。用于双向认证的公钥证书和私钥代表客户端身份,所以是隐秘的,一般都是用.p12或者client.crt+client.key进行存放。双向认证流程如下:

双向认证的安全性非常高,使用工具也不易破解,但由于增加了服务端校验成本,降低了响应速度,该方案比较适合对安全等级要求比较高的业务。

五、总结

以上列举的只是部分数据保护方案,每个业务根据自身对数据的安全的要求,选择适合自身的方案。此文仅从数据安全的一个方面来探讨如何做好数据防护,一方面分享几种数据防抓包的方案,另一方面可以引起大家对数据安全的重视和对数据安全方面的兴趣。

参考

转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。

关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~

相关推荐
BullSmall6 小时前
第三方软件安全-Dependency‑Track Docker 部署
安全·docker·容器
AImatters7 小时前
国产算力底座,支撑油气储运数字化迈过关键分水岭
安全·cpu·算力·海光·油气储运
叠叠乐10 小时前
中国移动家庭云电脑window关闭所有安全脚本
安全
Jmyd012310 小时前
实训室的 3D 模型涉及肖像文物,数据安全与合规怎么做?
安全·3d·数据安全·虚拟实训
芯盾时代10 小时前
《金融业网络安全管理办法(征求意见稿)》全条款深度拆解(三)
网络·安全·网络安全
敢敢是只喵i11 小时前
一个本地 AI Agent 要操作多个门店或 SaaS 账号,应该怎样安全切换身份?
人工智能·安全·ai·系统架构·业界资讯
Acrel123412 小时前
DC800V 大规模落地,算力机房安全该如何保障
安全
其实防守也摸鱼12 小时前
权限提升与横向移动:从内网渗透到域控的完整技术图谱
运维·服务器·数据库·安全·github·copilot·渗透
深圳海导科技13 小时前
资产防盗追踪系统:物联网高价值资产安全管理|海导科技navynav
科技·物联网·安全
波多利希最高礼仪的握力器13 小时前
安全复制SCP
linux·服务器·安全