合规安全检测点经验分享

合规安全检测点经验分享

本次分享的检测点

  • 加密算法
  • 密钥管理
  • 完整性
  • 抗抵赖
  • 证书校验

通讯协议介绍及抓包

    App与服务器交互就会涉及到信息的交换,而信息的交互就必然需要一套完整的数据协议。通信协议就是双方用来完成通信或服务所必须遵循的规则和约定。App逆向过程需要获取通讯数据来对业务功能进行分析,而抓包就是通过一些手段来获取这些需要分析的通讯数据,对不同的通讯协议的抓包需要的工具和方法也不通。
  • Hook抓包
    Hook抓包是指通过对发包函数(或对传输数据处理路径上的其他函数)进行Hook打印参数或返回结果的方式获取实际传输数据,以达到抓包的目的,常见于传输数据加密的情况。
  • 通讯传输时抓包
    使用抓包工具对通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与其直接对话,但事实上整个会话都被攻击者完全控制。

HTTPS协议抓包

介绍

    超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。可以理解为一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。HTTP协议在通信过程使用明文传输client请求和server响应的报文。因此存在信息窃听、信息篡改和信息劫持的风险。因此诞生出了HTTPS协议,HTTPS基于HTTP协议。

    HTTPS的安全基础是SSL/TLS。TLS 是介于TCP和HTTP之间的一层安全协议,不影响原有的TCP协议和HTTP协议,所以使用HTTPS基本上不需要对HTTP页面进行太多的改动。TLS为应用层提供安全服务,其目标是保证两个应用之间通信的保密性和可靠性,可在服务器和客户机两端同时实现支持。

    服务端返回公钥是明文的,攻击者可以使用伪造的公钥与客户端进行交互,那么客户端如何保障通讯对象是合法的呢?这里就使用到了数字证书。

Untitled

数字证书

    数字证书,这里指公钥证书 ( Public-Key Certificate, PKC) ,里面记有使用者的姓名、组织、邮箱地址等信息,以及属于此人的公钥,并由认证机构 (Certification Authority、 Certifying Authority, CA)施加数字签名 。

认证机构(CA)

    认证机构(CA)是数字证书认证中心的简称,是指发放、管理、废除数字证书的机构,CA的作用是检查证书持有者身份的合法性,并签发助手,以防证书被伪造或篡改,以及对证书和密钥进行管理:
  • 生成密钥对
  • 在注册公钥时对本人身份进行认证
  • 生成并颁发证书
  • 作废证书

证书链

    证书链是Root CA签发二级Intermediate CA,二级Intermediate CA可以签发三级Intermediate CA,也可以直接签发用户证书。从Root CA到用户证书之间构成了一个信任链:信任Root CA,就应该信任它所信任的二级Intermediate CA,从而就应该信任三级Intermediate CA,直至信任用户证书。

Untitled

服务端在发送明文公钥证书的情况下,客户端如何验证服务端的合法性呢?一般使用SSL pinning(证书绑定)的方式实现,这里提供两种方案:

  1. 校验站点证书的唯一性:

    一般可以校验站点证书的公钥、指纹、文件哈希值

  2. 校验上级证书的唯一性,另外校验站点证书的固定信息(在证书更新时不会变动的信息)

    由于站点证书的有效期较短,在更新证书后,使用第一种校验方案可能会导致旧版本的app无法正常使用,需要强制更新,为避免这种场景,需要一个时效长且有效的校验方案。首先校验根证书或中间证书的公钥、指纹、文件哈希值,根证书和中间证书的有效期较长(10-20年),可保障站点证书由可信CA颁发,之后再校验站点证书的域名、使用者信息等固定信息,可保障目标证书是由该CA颁发的指定站点证书。当然这种方案并不绝对安全,攻击者是可以通过向该CA购买证书,并将信息指定为App校验的信息,来绕过这种检测,但是需要增加攻击成本,权衡下我们认为这种方案是符合要求的。

这里给出对证书校验的几种策略:

安全等级 策略 信任范围 破解方法
0 完全兼容策略 信任所有证书包括自签发证书 无需特殊操作
1 系统默认策略 信任系统或浏览器内置CA证书以及用户安装证书 设备安装代理证书
2 System ca pinning 只信任系统根证书,不信任用户安装的证书(Android 7.0后系统信任策略) 注入或root后将用户证书拷贝到系统证书目录
3 CA Pinning
Root(Intermediate) cert pinning 信任指定CA颁发的证书 hook注入等方式篡改验证逻辑
4 Leaf Cert pinning 信任指定站点证书 hook注入等方式篡改验证逻辑
如遇到双向锁定需要将App通讯证书导入到抓包工具

抓包流程

  • burpsuite
  • charles
  • fiddler
  • whistle

抓包配置见附件《抓包流程介绍-Android-屏蔽代理》、《抓包流程介绍-Android》、《抓包流程介绍-iOS》

可能遇到的错误1

设备设置代理后,发现无法正常访问,在抓包工具中会看到如下信息:

Untitled

需要设备信任抓包工具证书。

可能遇到的错误2

完成对抓包工具的信任后,会发现部分app能够正常抓取通讯数据,但是仍然有部分app存在报错:

Untitled

这种情况一般就是app内存在对SSL证书的校验了,抓包工具在与app通讯前会使用请求目标站点的站点证书信息,使用自己的根证书颁发一个伪造站点证书用于与app的通讯。那么伪造证书和真实证书之间的差异就是check的目标了。

解决方式:

  • 消除证书差异;(证书私钥泄露等一般无法实现)
  • 绕过check过程;(见上文证书校验策略里的破解方法)

常见的HTTP组件的SSL pinning方法

Android

Android开发中会使用谷歌官方提供的HTTP通讯组件或其他第三方的开源组件。常见的组件有httpURLConnection、httpClient、Okhttp3\Retrofit等。

HttpURLConnection

HttpURLConnection是Android自带的HTTP网络通讯库。
其基本使用方法:
使用请求url新建一个URL对象,使用openConnection()函数获取一个HttpURLConnection实例。

1
2
URL url = new URL({"https://www.f4ncy.top")
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

SSL pinning with HttpUrlConnection

通过HttpsURLConnection实例设置包含校验逻辑的SSLSocket来代替默认的SSLSocket。
SSLSokcet的创建一般通过获取SSLContext实例后,将实现SSL证书信息的证书管理器传入。

1
2
3
4
5
6
7
8
// 创建SSLContext对象,并使用我们指定的信任管理器初始化 
TrustManager[] tm = { MyTrustManager()}; //实现证书信息校验的TrustManager
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");// ("TLS");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
HttpsURLConnectionconnection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(ssf);

okhttp3

Okhttp3是目前比较火的http客户端,用于android和java项目开发中的网络通讯组件。
Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装,网络请求的内部实现是以 OkHttp3 完成,而 Retrofit 仅负责网络请求接口的封装。

先创建一个okhttpClient对象,再创建一个Request对象,okhttpClient客户端将Request对象封装成Call对象后,就可以调用enqueue()函数发起一次网络请求:

1
2
3
4
5
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().xxx.build();
okHttpClient.newCall(request).enqueue(new Callback() {

}

SSL pinning with okhttp3

Okhttp3的ssl绑定实现方式一般是通过添加拦截器的形式,在OkhttpClient初始化时可以设置是否代理、SSL验证、协议版本等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

//方式一:绑定certificatePinner 实现简单

CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("api hostname", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient okHttpClient = new OkHttpClient.Builder().certificatePinner(certificatePinner).xxxxxxx.build();

//方式二:设置sslSocketFactory 校验方式较为灵活
OkHttpClient build = new OkHttpClient.Builder()
.sslSocketFactory(getSocketFactory(), getX509TrustManager())
.xxxxxxx.build();

public final SSLSocketFactory getSocketFactory() {
try {
Certificate generateCertificate = CertificateFactory.getInstance("X.509").generateCertificate(your cert file);
KeyStore instance = KeyStore.getInstance(KeyStore.getDefaultType());
instance.load((InputStream) null, (char[]) null);
instance.setCertificateEntry("ca", generateCertificate);
TrustManagerFactory instance2 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
instance2.init(instance);
TrustManager[] trustManagers = instance2.getTrustManagers();
SSLContext instance3 = SSLContext.getInstance("TLS");
instance3.init((KeyManager[]) null, trustManagers, new SecureRandom());
return instance3.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

OkHttpClient okHttpClient = OkHttpClient.Builder()
.newBuilder()
.proxy(Proxy.NO_PROXY)//屏蔽代理
.xxxx
.sslSocketFactory(getSocketFactory(), getX509TrustManager())
.build();

iOS

AFNetwork

  1. 新建一个manager;
  2. 在mainBundle中找到证书文件https.cer, 并且将相关的数据读取出来;
  3. 新建一个AFSecurityPolicy,并进行相应的配置;
  4. 将这个AFSecurityPolicy 实例赋值给manager.

AFSSLPinningModeNone: 代表客户端无条件地信任服务器端返回的证书。
AFSSLPinningModePublicKey: 代表客户端会将服务器端返回的证书与本地保存的证书中,PublicKey的部分进行校验;如果正确,才继续进行。
AFSSLPinningModeCertificate: 代表客户端会将服务器端返回的证书和本地保存的证书中的所有内容,包括PublicKey和证书部分,全部进行校验;如果正确,才继续进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
+ (AFSecurityPolicy*)customSecurityPolicy
{
// /先导入证书
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"certfile" ofType:@"cer"];//证书的路径
NSData *certData = [NSData dataWithContentsOfFile:cerPath];

// AFSSLPinningModeCertificate 使用证书验证模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

// allowInvalidCertificates 是否允许无效证书(也就是自建的证书),默认为NO
// 如果是需要验证自建证书,需要设置为YES
securityPolicy.allowInvalidCertificates = YES;

//validatesDomainName 是否需要验证域名,默认为YES;
//假如证书的域名与你请求的域名不一致,需把该项设置为NO;如设成NO的话,即服务器使用其他可信任机构颁发的证书,也可以建立连接,这个非常危险,建议打开。
//置为NO,主要用于这种情况:客户端请求的是子域名,而证书上的是另外一个域名。因为SSL证书上的域名是独立的,假如证书上注册的域名是www.google.com,那么mail.google.com是无法验证通过的;当然,有钱可以注册通配符的域名*.google.com,但这个还是比较贵的。
//如置为NO,建议自己添加对应域名的校验逻辑。
securityPolicy.validatesDomainName = NO;

securityPolicy.pinnedCertificates = @[certData];

return securityPolicy;
}

AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
mgr.responseSerializer = [AFHTTPResponseSerializer serializer];
[mgr setSecurityPolicy:[self customSecurityPolicy]];

对于自定义证书的验证,参考NSURLSession。

NSURLSession

该类通过[[NSURLSessionDelegate URLSession:didReceiveChallenge:completionHandler:]](https://developer.apple.com/documentation/foundation/url_loading_system/handling_an_authentication_challenge/performing_manual_server_trust_authentication)
委派方法来实现证书验证回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {

//得到远程证书
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, 0);
//设置ssl policy来检测主域名
NSMutableArray *policies = [NSMutableArray array];
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)challenge.protectionSpace.host)];
//验证服务器证书
SecTrustResultType result;
SecTrustEvaluate(serverTrust, &result);
BOOL certificateIsValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);
//得到本地和远程证书data
NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));

NSString *pathToCer = [[NSBundle mainBundle] pathForResource:@"certfile" ofType:@"cer"];

NSData *localCertificate = [NSData dataWithContentsOfFile:pathToCer];

//检查
if ([remoteCertificateData isEqualToData:localCertificate] && certificateIsValid) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}else {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,NULL);
}
}

其他

其他关于HTTPS服务器信任评估方法可以参考apple官方文档:

Technical Note TN2232: HTTPS Server Trust Evaluation (apple.com)

当我们成功抓取到app通讯数据后,就可以分析其参数内容,了解各个功能的业务逻辑了。但是,我们发现有时抓取到的数据全部是加密的,那么分析前就需要了解其加密过程:

加密算法

类型介绍

对称加密

目前主流对称加密算法:AES、SM4、3DES等

不安全的对称加密算法:RC4、DES等

不安全的加密模式:ECB模式

非对称加密

RSA、SM2等

加密代码定位方式

  1. 通过关键词定位

    例如URL、参数名称、函数名特征

    首先需要判断该请求所在原生代码还是Webview,一般可以通过获取页面layout判断当前页面是原生页面还是webview。(adb shell dumpsys activity activities | grep mResumedActivit)
    对可能存在位置进行关键词搜索,如果是原生的,反编译apk后进行搜索,一般来说会在dex中或者资源文件中,如果搜不到可能进行了字符串加密,在so文件中的情况很少,开发者不太可能使用C/C++编写业务接口处理的代码,当然也有一些框架可能会使用脚本编写业务代码,并编译进so中;如果是H5的,需要判断是本地离线H5还是在线H5动态加载,可以通过hook webview的loadUrl函数获取加载的url(file://xxx、https://xxx)。对于离线的,有打包进apk和下载两种,打包进去的可以在assets目录下寻找html、js文件进行搜索,下载下来的可以在私有目录file或其他目录下搜索;在线H5的,一般通过抓包可以获取到具体url,在浏览器中访问下载。
  2. 上下文查找http构造链
    如果遇到加密、混淆,无法通过关键词进行定位,需要逆向代码了解其HTTP请求具体代码,通过分析HTTP Request构造链来定位参数处理和url还原,一般对Response的处理也会在构造链完成后的调用代码的callback处。
  3. 通过堆栈记录寻找 使用profile、simpleperf等跟踪进场,通过记录某个接口事件(点击一次登录按钮)(尽可能缩短记录的执行窗口,避免trace过大,增大分析难度),在trace中搜索dofinal、encrypt、Base64、request等关键词进行定位,这种方式找到的目标点可能有很多,需要结合app代码和hook尝试来定位正确目标函数; 使用frida hook加密库的锚点函数,例如dofinal、javax.crypto.Cipher.getInstance,根据堆栈信息搜索可疑函数。
  4. 常见框架特征 柯蓝(包名为com.csii.xxx) 屹通(包名为com.yitong.xxx) mpaas TMF 例: 柯蓝AES(旧) com.csii.njaesencryption.PEJniLibRSA(加密工具类) libcsii_AESTelecomModule_v1_0.so(加密实现动态库) 生成AES随机密钥,setNativeAesKeyMode第一个参数为1,生成128位长度密钥,若为2,生成256位长度密钥。 Untitled 数据加密使用CBC模式的AES加密算法: Untitled test最终报文拼接 Untitled

如何判断算法类型:

  1. 常量参数,例如“AES/CBC/paddingmode”
  2. 函数符号
  3. 配置文件,部分框架会在特定的配置文件中设置加密算法类型
  4. 算法特征:特征值、密钥长度、密文长度

    AES密钥长度为128、192、256bit;3DES为168bit;SM4为128、256bit

    例如AES加密中使用到的TBOX(查表法)中的常量:0xC66363A5等

  5. 利用插件

    findcrypt3

密钥管理

  1. 密钥生成

    要保证不同用户使用不同密钥加密,一般通过客户端本地随机生成密钥的方式实现,建议使用安全的随机数生成器,例如java.security.SecureRandom类

  2. 密钥传输

    客户端生成的对称密钥需要共享给服务端,服务端才能解密客户端请求的数据,并加密应答数据后返回给客户端。对称密钥如何安全地共享给服务端:
    a. 使用非对称加密算法加密对称加密密钥;
    b. 使用安全的密钥协商方式,例如Diffie-Hellman密钥交换。

  3. 密钥保存

    对于数据通讯时使用的加密密钥,一般为一次一密或启动时生成密钥,这种情况下密钥是不需要落地存储的,在加密工具类中使用成员变量维持和传递即可;

    对应本地数据存储时使用的加密密钥,由于其加解密逻辑均在本地实现,可通过逆向分析获取到密钥,此时需要安全的密钥保存方式,Android Keystore利用设备的可信任执行环境(TEE),开发者可以使用KeyStore API生成密钥、使用密钥签名、使用密钥加解密、获取密钥的属性信息,但无法将密钥本身从KeyStore中取出,密钥不会出现在应用进程。

完整性

我们一般使用单向散列函数,也被称为消息摘要函数或哈希函数,来计算消息摘要,用于数据的完整性校验。

安全的散列函数应具备的性质:

  • 抗碰撞性。根据一个输入,找到一个其它输入得到相同的输出,在计算上是不可行的;
  • 不可逆性。根据一个输出,找到一个输入其散列值等于输出,在计算上是不可行的,即不可能从结果逆向推导初始值。

类型介绍

MD5、SHA-1、SHA-2、SHA-3、SM3

MD5以及SHA1的强抗碰撞性已被证明不满足。

抗抵赖性

消息认证码(MAC)

    由于单项散列函数仅能保证数据的的完整性,即数据在传输过程中没有被篡改,而无法证明数据是由正确的发送者发送,即无法做到对数据来源的认证。

    消息认证码是在单项散列函数的基础上增加了加密函数,发送者和接收者共享一个密钥,在密钥保密的情况下,接收者可以验证数据来自于特定的发送者,且数据未被篡改。

    但是由于发送者和接收者使用共享的密钥,即双方可以伪造对方的消息、否认自己的消息,对于资金交易等重要的消息,需要保障交易双方消息来源的合法性,这里使用到了数字签名技术。

数字签名

    数字签名就是利用了“没有私钥的人事实上无法生成使用该私钥所生成的密文”这一性质来实现。

    数字证书基于公钥,其带有使用者的详细信息,并且其基于公钥基础设施(PKI)的架构,让其具有完善的管理体系,是目前来验证消息来源合法性的主要载体。(密钥的分发需要一个三方机构的参与,一般来说证书服务器充当了该场景下的CA)

Untitled

    数字签名使用的算法有RSA、ElGamal、DSA、ECDSA、Rabin,RSA是目前数字签名中使用最普遍的算法。

常见流程:

  1. 客户端上送设备信息、用户信息向证书服务器获取随机数;
  2. 证书服务器返回随机数;
  3. 客户端上送随机数、设备信息、用户信息,交易服务器组装随机数和用户信息,并使用机构证书签名该数据;
  4. 客户端获取该签名结果,生成证书列表对象,并进行身份认证,通过后上送证书列表给对象证书服务器,证书服务器返回用户证书状态;
  5. 客户端根据返回状态判断是否具有证书,若无证书,则申请下载证书;
  6. 若有证书,客户端向交易服务器获取随机数,生成客户端随机数,收集交易信息传输给交易服务器,服务端组装交易报文,使用机构证书对报文进行签名;
  7. 客户端将交易报文和服务端返回的机构签名结果进行组装,使用个人证书对其签名(部分方案需要签名服务器与客户端进行协同签名);
  8. 将签名结果与交易信息一同发送给证书服务端,证书服务端验证签名结果。

引用:

https://blog.csdn.net/qq_43589852/article/details/121457771

https://zhuanlan.zhihu.com/p/440612523

https://www.jianshu.com/p/4102b817ff2f

《图解密码技术》