Firebase Installations API 密钥硬编码

Firebase Installations API 密钥硬编码

image

起因

新公司让我对应用移动端产品也渗透一下,自然地想到移动端APK相关的自动化检测工具,于是Docker起了一个MobSF跑了下检测,算是做个铺垫。

当然还不如自己手动测来的漏洞直接,自动化扫描得出的结果不一定都具有直接性和威胁性,但MobSF还支持动态检测,实测下来确实能自动化访问activity事件,但每次调用Frida去执行注入测试的时候就会出大大小小的问题:

  1. 目前Mac M1/M2芯片由于ARM架构的问题,还无法完全解决Genymotion跑不了ARM包的问题(Genymotion给的MAC客户端实现用的是X86架构,完全不是官方说的因为是ARM芯片就可以直接支持ARM包了,实践出真知),M1/M2可使用的系统目前只有Android11,无法打translation包
  2. 很多移动应用做了SSL-Pinning等方式防抓包,需要用各种Posed去解,这些操作可能会与MobSF动态检测的模块冲突与覆盖(如MobSF动态检测会自动植入证书)

虽然对个人的工作不会带来太大的影响(又不是没有扫描器就不会渗透了),但我还是想流畅地使用一回动态检测啊,看看能达到什么层度,有这个执念在,我萌生了一个想法:

互联网上有没有别人搭建好的完整检测平台呢?

image

资产发现

还真被我找到一个,而且像是官方搭建的站点,此处相关域名隐去,不予展开。

作为一个安全人员,自然会考虑提交检测的包会上传存储在哪及检测内容是否会暴露的问题,于是我先下意识地访问了recent scans:

image

这是啥?!为什么我能看到今天所有提交检测的应用,并且可以看到完整的检测报告?!而且更哈人的是,举例应用竟然是未对外发布的内部研发版本(官网版本2.0.011 < 研发版本2.0.015),且没有做加固之类的!

image

这不禁让我想到了两个问题:

  1. MobSF这个线上检测平台肯定是没做好权限划分的,或者从设计来说MobSF可能就没实现这点,因为一般都是在本地部署的检测平台;纵使在后台做了定期清理数据的策略,在短期内这些检测内容还是会留存下来
  2. 所有被检测的应用可以被任意的人员查看,如果有安全问题很有可能线上版本也存在类似问题,而且数据泄漏就是泄漏了,在一段时间内一个访问凭证都将是有效的,即使你知道它已经泄露,会因为你不知道多少功能调用用到了这个凭证而不敢直接替换

那么这个应用里有什么呢?

image

API Key泄露

直接上图,这些码应该够了吧:

image

除了显而易见的google_maps_key 外,apk内还硬编码了firebase_database_urlgoogle_api_keygoogle_app_idgoogle_storage_bucket 等敏感参数(有些是我直接获取源包找的),firebase_database_url 直接访问果然无权限,但是剩下的几个参数是怎么调用的?能获得什么信息呢?

首先猜测和存储空间有关,但不对,Google Cloud并不是用这样的参数类型请求访问的;那既然存在firebase_database_url,会不会和Firebase有关?一番文档翻找下,我大致了解了Firebase是啥,还有一部分的API调用,并知道了相关的API Key是在什么位置被生成又用于何处的:

image

之后要做的事情就简单了,构造一个请求,就可以获取到与Firebase服务器交互许可的凭证了:

image

之后的操作就不做了,扩大影响面应该很容易,所以为什么要演奏春日……啊不对,为什么要把应用程序的报告暴露在公开检测平台!

image

参考引用

Deploy an application - Desktop User Guide

How to install Xposed/EdXposed/LSPosed + Magisk with Genymotion Desktop?

管理 Firebase 安装

排查初始化选项问题

与 Firebase 安装服务器 API 通信时的 Android 错误

与 Firebase 安装服务器 API 通信时的 Android 错误

本文转自Segmentfault 并作补充

Questions

我在应用程序启动时收到一条错误消息,说明日志如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
W/Firebase-Installations: Error when communicating with the Firebase Installations server API. HTTP response: [400 Bad Request: {
"error": {
"code": 400,
"message": "API key not valid. Please pass a valid API key.",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.Help",
"links": [
{
"description": "Google developers console",
"url": "https://console.developers.google.com"
}
]
}
]
}
}
]
2020-04-27 12:42:34.621 22226-23596/in.co.androidapp.g7 E/Firebase-Installations: Firebase Installations can not communicate with Firebase server APIs due to invalid configuration. Please update your Firebase initialization process and set valid Firebase options (API key, Project ID, Application ID) when initializing Firebase.

大约一周前我收到一封电子邮件,我应该更新我的 google_services.json 文件,我已经更新了 4-5 次。没有改进。它已经工作了大约一年。自从我在应用程序中遇到此问题以来只有 2-3 天。

随后,Firebase Cloud Messaging 和其他 Firebase 服务无法正常工作。我没有进行编程初始化(即,使用 FirebaseOptions 对象提供这些值),只是默认初始化使用 FirebaseApp.initializeApp(this);

我试过 https://github.com/firebase/firebase-android-sdk/blob/master/firebase-installations/API_KEY_RESTRICTIONS.md

提前致谢。

原文由 Daksh Agrawal 发布,翻译遵循 CC BY-SA 4.0 许可协议

Answers

如果您的 API 密钥有问题,您可以在 Cloud Console 中创建一个新的 API 密钥:

  • 转到 谷歌云控制台
  • 选择相关项目(即您用于申请的项目)
  • 打开菜单并转到 APIs & ServicesCredentials
  • 在页面顶部点击 + CREATE CREDENTIALSAPI key
  • 用新创建的 API 密钥替换应用程序中的 API 密钥

如果您使用 google-services.json 来自 Firebase 控制台 的配置文件,您首先必须删除或限制当前 google-services.json 中使用的 API 密钥,以便使 Firebase 更新配置文件和使用新的 API 密钥。

  • 在您的 google-services.json 配置文件中识别 API 密钥。
  • 通过根据 Firebase Installations API 指标页面 检查 API 密钥的使用情况,确认 API 密钥正在创建错误请求。 API 密钥的列 Usage with this service 应显示大于 0 的数字。
  • 通过单击 bin 符号删除该 API 密钥,或通过单击铅笔符号将 Application restrictions 添加到该 API 密钥。 !!警告!! 不要删除应用程序现有安装所需的 API 密钥,以用于其他 Firebase 服务,例如 Firebase Auth 或 Realtime-Database。

等待几分钟,让 Google 服务器更新。下次下载 google-service.json 配置文件应该包含一个新的 API 密钥。

您可以使用以下 CURL 命令测试您的配置。你得到的 错误 是什么? (注意:如果您看到的是 JSON 数据,则说明您的配置成功)

测试您的配置是否适用于以下 CURL 命令:

1
2
3
4
5
 api_key=<YOUR_API_KEY>;
project_identifier=<YOUR_PROJECT_ID>;
app_id=<YOUR_FIREBASE_APP_ID_EXAMPLE_1:12345678:android:00000aaaaaaaa>;

curl -H "content-type: application/json" -d "{appId: '$app_id', sdkVersion: 't:1'}" https://firebaseinstallations.googleapis.com/v1/projects/$project_identifier/installations/?key=$api_key;

关于 API 密钥和 Firebase Installations API 的其他相关链接:

原文由 Andreas Rayo Kniep 发布,翻译遵循 CC BY-SA 4.0 许可协议

阿里热修复Sophix的使用指南

阿里热修复Sophix的使用指南

本文转自五问 并作补充

一、阿里云热修复Sophix的介绍

1.1、首先看一下市场上热修复方案的比较:

https://help.aliyun.com/document_detail/434850.html

image

1.2、收费情况:

https://help.aliyun.com/document_detail/434858.html

image

二、接入指南

2.1、准备

  1. 准备好阿里云账号
  2. 进行身份实名认证
  3. 移动研发平台EMAS,进行项目创建,创建后会生成一个aliyun-emas-services.json 文件,里面包含了热修复sdk接入时所需的密钥,很重要

2.2、EMAS平台的添加流程如下:

2.2.1、添加应用

image

image

生成应用的key信息文件,如果建议按需接入所需功能,没必要也不安全,按下图将文件放入到app中

image

下图的红框中的添加sdk也是,添加emas平台中的所有功能,建议按需接入某个功能(如:热修),另外该插件在Android gradle 7之上会报错

image

2.3、热修复接入

https://help.aliyun.com/document_detail/434883.html

注意点如下:

使用gradle plugin版本高于4.2时,可能会自动开启资源优化。开启资源优化后,资源名称被混淆,会导致补丁工具在生成补丁时一直卡在”开始构建补丁…..”,无法正常解析apk包。解决方案:在gradle.properties 中新增android.enableResourceOptimizations=false,重新生成基线包和修复包,然后再生成补丁。

密钥的使用推荐在SophixStubApplication 中初始化,而不是在AndroidManifest文件中配置

使用android studio打包生成apk时,要关闭instant run。

queryAndLoadNewPatch方法用来请求控制台发布的补丁包,会涉及设备信息读取,所以必须在用户同意隐私协议之后调用。另外用户可根据业务情况,酌情考虑是否打开此开关。

但不可放在attachBaseContext中,否则无网络权限,建议放在主进程用户同意隐私协议之后的任意时刻。

接入流程步骤如下:

添加工程依赖

1. Android Studio集成方式

1
2
3
4
5
6
7
8
9
10
11
12
13
gradle远程仓库依赖, 打开项目找到App的build.gradle文件,添加如下配置:

添加Maven仓库地址:

****

```
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
}
```

2.添加gradle坐标版本依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
android {
......
defaultConfig {
applicationId "com.xxx.xxx" //包名
......
ndk {
//选择要添加的对应cpu类型的.so库。
//热修复支持五种
abiFilters 'arm64-v8a', 'armeabi', 'armeabi-v7a', 'x86', 'x86_64'
}
......
}
......
}
dependencies {
......
compile 'com.aliyun.ams:alicloud-android-hotfix:3.3.5'
......
}

3.添加应用权限

Sophix SDK使用到以下权限,使用Maven依赖或者aar依赖可以不用配置。具体配置在AndroidManifest.xml中。

1
2
3
4
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

4.配置AndroidManifest文件

也可以不用配置(直接在SophixStubApplication,文件中初始化配置即可(推荐在SophixStubApplication中配置)),在 移动研发平台EMAS,进行项目创建,创建后会生成一个aliyun-emas-services.json 文件,里面包含了热修复sdk接入时所需的密钥

AndroidManifest.xml中间的application节点下添加如下配置:

1
2
3
4
5
6
7
8
9
<meta-data
android:name="com.taobao.android.hotfix.IDSECRET"
android:value="App ID" />
<meta-data
android:name="com.taobao.android.hotfix.APPSECRET"
android:value="App Secret" />
<meta-data
android:name="com.taobao.android.hotfix.RSASECRET"
android:value="RSA密钥" />

5. 混淆配置,按需配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
```
#基线包使用,生成mapping.txt
-printmapping mapping.txt
#生成的mapping.txt在app/build/outputs/mapping/release路径下,移动到/app路径下
#修复后的项目使用,保证混淆结果一致
#-applymapping mapping.txt
#hotfix
-keep class com.taobao.sophix.**{*;}
-keep class com.ta.utdid2.device.**{*;}
#防止inline
-dontoptimize
```
**
**重要**

开启混淆时,生成修复包要使用旧包的mapping文件以保证混淆结果一致。
初始化

6. 初始化

初始化的调用应该尽可能的早,必须在Application.attachBaseContext()的最开始(在super.attachBaseContext之后,如果有Multidex,也需要在Multidex.install之后)进行SDK初始化操作,初始化之前不能用到其他自定义类,否则极有可能导致崩溃。而查询服务器是否有可用补丁的操作可以在后面的任意地方。不建议在Application.onCreate()中初始化,因为如果带有ContentProvider,就会使得Sophix初始化时机太迟从而引发问题。

Sophix最新版本引入了新的初始化方式。

原来的初始化方式仍然可以使用。只是新方式可以提供更全面的功能修复支持,将会带来以下优点:

  • 初始化与应用原先业务代码完全隔离,使得原先真正的Application可以修复,并且减少了补丁预加载时间等等。
  • 新方式能够更完美地兼容Android 8.0以后版本。
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class SophixStubApplication extends SophixApplication {
private final String TAG = "SophixStubApplication";
String appVersion = "0.0.0";

// 此处SophixEntry应指定真正的Application,并且保证RealApplicationStub类名不被混淆。
@SophixEntry(MyApplication.class)
static class RealApplicationStub {
}

@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
initSophix();
}

private void initSophix() {
try {
appVersion = this.getPackageManager()
.getPackageInfo(this.getPackageName(), 0)
.versionName;
} catch (Exception e) {
}
final SophixManager instance = SophixManager.getInstance();
List<String> tags = new ArrayList<>();
///todo 构建正式包之前需要修改为:
///tags.add("production");
tags.add("test"); //这个值就是,你进行灰度包热修时,要填写的tag
for (String tag : tags) {
Log.e(TAG, "sophix tag:" + tag);
}
instance.setContext(this)
.setAppVersion(appVersion)
.setEnableDebug(false)
.setEnableFullLog()
.setTags(tags)
.setSecretMetaData("你自己的hotfix.idSecret",
"你自己的emas.appSecret",
"你自己的hotfix.rsaSecret")
.setPatchLoadStatusStub(new PatchLoadStatusListener() {
@Override
public void onLoad(final int mode, final int code, final String info, final int handlePatchVersion) {
Log.e(TAG, "sophix handlePatchVersion:" + handlePatchVersion + " /code:" + code + " /info:" + info);
if (code == PatchStatus.CODE_LOAD_SUCCESS) {
///重启后会装载补丁会调用该方法
Log.e(TAG, "sophix load patch " + handlePatchVersion + " success!" + info);
} else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
///补丁加载成功会调用该方法
// 如果需要在后台重启,建议此处用SharePreference保存状态。
Log.e(TAG, "sophix preload patch success. restart app to make effect. handlePatchVersion:" + handlePatchVersion);

Intent intent = new Intent();
intent.putExtra("message", "恭喜你,成功接收到发送的广播" + PatchStatus.CODE_LOAD_RELAUNCH);
intent.setAction("可使用重启广播重启");
sendBroadcast(intent);
}
}
}).initialize();
}

}

7.最后,需要把AndroidManifest里面的application改为这个新增的SophixStubApplication类:

1
2
3
4
<application
android:name="com.my.pkg.SophixStubApplication"
... ...>
... ...

这样便完成了新方式的初始化接入改造。

三、热修复打差量包流程 热修控制台地址

https://emas.console.aliyun.com/service/devTool/hotfix/patch

3.1、找到热修界面添加应用

如下图:注意应用版本必须与你的versionName一致,否则会导致后续下发查找不到差量包

image

image

3.2、打差量包

https://help.aliyun.com/document_detail/434864.html

  1. 准备好SophixPatchTool_windows打包工具各版本打包工具如下:
  1. 打一个release的包
  2. 再第2步的release包的基础上修改一些内容,在打一个release包
  3. 利用工具开始打包,如下图

注意: 每次热修差量包的生成的基础包就是你上次发版后的包,并不是说你在进行第三次热修时,用第二次的热修包,作为基准包

问题包:1.0-querelease-2022-12-07.apk,修复包:1.0-release-2022-12-07.apk

image

打开工具:

image

选择两个包填充

image

选择设置填好keystore

image

选择高级,默认如下图,强制冷启动就是补丁包下发后必须重启,建议勾选强制冷启动,为何:看下面解释

image

Sophix何时走即时生效热修复,何时走冷启动修复?https://help.aliyun.com/document_detail/53227.htm?spm=5176.2020520104.0.0.385a3f1bbai0Ta#topic-1993907

配置完成后点击Go开始打差量包

image

成功

image

sophix-patch.jar 就是打出的差量包

image

四、上传补丁-发布

4.1、添加你的应用版本,然后上传补丁

image

4.2、发布补丁

上传后点击发布如下图

image

下载[hotfixdebug]工具验证你的补丁包是否成功,调试流程原文如下链接:

https://help.aliyun.com/document_detail/434866.html

image

调试没问题后点击上图的新建发布

image

发布的话:有全量,灰度两种,灰度的话 ,需要设置指定标签tag,就是你的应用app中Sophix所配置的,如下图:(推荐是全量)

image

4.3、发布成功后在app中调用查询补丁方法

SophixManager.getInstance().queryAndLoadNewPatch()

后续将回调PatchLoadStatusListener如下图

image

依次出现热修状态码如下,状态码含义:

https://help.aliyun.com/document_detail/434886.html?spm=a2c4g.11186623.0.0.6cdf4b0cnRYOiA

image

image

上图加载成功后提示需要重启后热修生效

五、测试用例

1. 修改一个toast文案,然后修复成功。

2. 修改一张图片,增加一张新的图片进行展示,增加点击效果,增加一个新的类生成提示文案(mipmap-hdpi、drawable-xhdpi、drawable、都添加图片)

drawable-xhdpi 中新增热修图片添加可能会存在问题:如下,然后按要求在drawable 中添加后,热修成功。

https://help.aliyun.com/document_detail/338384.html

image

3. 添加assets文件,热修成功。

4. 四大组件不行,因为需要在AndroidManifest.xml文件中配置,热修的时候使用的话会报找不到的,需要注册异常,如下图

image

5. 删除测试4中的组件跳转流程,热修测试成功,

6. 删除测试2中添加的xml中的代码与相关资源,热修测试成功

注意:

1.热修包存在的话,每杀死app,初始化进来都会走

1
2
3
onLoad方法的回调

code == PatchStatus.CODE_LOAD_SUCCESS

image

2.同一个热修包,设置不同的tag,然后点击下发的话都会下发一次

3.清除热修包后,之前发布的版本都无效了,再次查询加载,都查不到了,只能之后在这个版本重新发布

1
SophixManager.getInstance().cleanPatches()

热修复书籍:https://developer.aliyun.com/article/115122

安全课堂|关于小程序session_key泄露漏洞

安全课堂|关于小程序session_key泄露漏洞

本文转自微信团队 并作补充

为进一步提升小程序的安全性和用户体验,目前平台对提审的小程序均需进行安全检测,在检测过程中发现仍有许多小程序存在安全漏洞,其中涉及session_key泄露漏洞,希望通过以下相关的漏洞介绍、案例分析和修复建议,开发者能更加了解如何对该漏洞进行防御。

一、漏洞介绍

为了保证数据安全,微信会对用户数据进行加密传输处理,所以小程序在获取微信侧提供的用户数据(如手机号)时,就需要进行相应的解密,这就会涉及到session_key,具体流程可参考开放数据校验与解密开发文档。

session_key指的是会话密钥,可以简单理解为微信开放数据AES加密的密钥,它是微信服务器给开发者服务器颁发的身份凭证,这个数据正常来说是不能通过任何方式泄露出去的。小程序若存在session_key泄露漏洞的情况,则代表微信侧传递的用户数据有被泄露、篡改等风险,开发者应及时发现该漏洞并快速修复相应问题。

image

二、漏洞案例

某小程序因为session_key泄露,导致该小程序可以使用任意手机号进行登录,造成了极大的安全风险。

我们可以很明显地看到,下列请求中的session_key已经被泄露:

image

通过获取该session_key,我们可以结合iv解密出密文:

image

只需如下脚本即可进行解密,所以攻击者也可利用同样的信息去篡改用户数据,然后加密后返回给服务器,从而达到使用任意手机号进行登录的目的。

image

三、漏洞修复

通过上述案例,我们了解到session_key泄露会对小程序造成的危害,而导致session_key泄露的原因则可能有以下两种:

1.通过auth.code2Session接口获取用户openid时,返回小程序的数据中包含了session_key字段,以泄露的url:/api/get_openid.php?code=xxxx为例,具体的表现如下图所示:

image

查看后端get_openid.php的源码,经排查发现$response 变量包含了session_key字段,开发者应去掉变量中的session_key字段,若需获取openid,应只提取该字段返回小程序即可。

image

2.在解密开放数据时,使用了错误的方式,以获取手机号接口为例,通过事件回调获取微信服务器返回的加密数据(encryptedData和iv)后,将服务端中的session_key传送至小程序前端,直接在前端进行解密:

image

这种方式是绝对不可取的,正确的流程应该是将加密数据(encryptedData和iv)传至服务端后,结合服务端中的session_key进行解密获取手机号,然后返回给小程序。另外,目前平台已对获取手机号接口进行了安全升级,建议开发者使用新版本,以增强小程序的安全性。

若小程序存在相应的session_key泄露漏洞问题,请开发者尽快自查并修复漏洞:

请尽快在网络请求中,去除请求和响应中的session_key字段及其对应值,后续也不应该将session_key传到小程序客户端等服务器外的环境,以便消除风险。

其他常见问题

Q1: 如何进行相应的修复,是需要把session_key字段更换个名字就可以了吗?

A1: 不是,更换字段名无法从根本上消除风险,session_key这个字段及对应值不应该传到小程序客户端等服务器外的环境,需去除请求和响应中的所有相关信息,才可对该漏洞问题进行修复。

Q2: 解密开放数据的正确方式是什么?

A2: 以获取手机号接口为例,通过事件回调获取微信服务器返回的加密数据(encryptedData和iv),将加密数据传至服务端后,结合服务端中的session_key进行解密获取手机号,然后返回给小程序。而不应将服务端中的session_key传送至小程序前端,直接在前端进行解密。

相关文章

安全课堂|关于小程序AppSecret密钥泄露漏洞

安全课堂|关于小程序云AK/SK泄露漏洞

敏捷代码审计之代码硬编码

敏捷代码审计之代码硬编码

本文转自Sanduo 并作补充

介绍

硬编码(英语:Hard Code或Hard Coding)是指在软件编码过程中,将输出或输入的相关参数(例如:路径、输出的形式或格式)直接以常量的方式撰写在源代码中,而非在执行期间由外界指定的设置、资源、资料或格式做出适当回应。一般被认定是种反模式或不完美的实现,因为软件受到输入资料或输出格式的改变就必须修改源代码,对客户而言,改变源代码之外的小设置也许还比较容易。但硬编码的状况也并非完全只有缺陷,因某些封装需要或软件本身的保护措施,有时是必要的手段。除此之外,有时候因应某些特殊的需求,制作出简单的应用程序,应用程序可能只会执行一次或者有限的几次,或者永远只应付某种单一需求。
所谓硬编码,就是把一个本来应该(可以)写到配置信息中的信息直接在程序代码中写死了。密钥硬编码在代码中,而根据密钥的用途不同,这导致了不同的安全风险,有的导致加密数据被破解,数据不再保密,有的导致和服务器通信的加签被破解,引发各种血案。

硬编码检测方法

方法论主要参考:密码密钥硬编码检查,参考链接详见文章末尾。

鉴别密码密钥方法

香农熵(Shannon entropy)

密钥的长度决定了密钥空间(keyspace),通常以位为单位。密钥空间越大,密钥被攻破的难度就越大。
密钥是由密钥空间的随机值构成。对于任意一个随机变量 X,它的熵定义如下:

image

变量的不确定性越大,熵也就越大,把它搞清楚所需要的信息量也就越大。

  • P(x_i)P(xi) : 指的是单个样本变量所属的变量种类的个数占据所有变量个数的比例。同等长度的字符串,通常密钥的熵值更高
  • 密钥为避免彩虹攻击,在取值上更加的离散,会尽量采用不重复的字符。就像我们为了增加密码的复杂性,要求长度不小于8,必须包含大小写、特殊字符、以及数字一样的道理。所以密钥的熵值会比一般的文本要高的多。我们就是利用这点来识别字符串是否是密钥。

工具的检查逻辑

对于密码密钥的硬编码检查可以采用静态分析工具来完成。工具的检查过程通常包含四个过程:输入文件准备、检查、过滤和报告输出。

image

输入文件分类

我们需要检查的文本文件进行分类,通常包括以下几种类型:

  • 程序语言:C、C++、Java、Python、Go、Js等;
  • 有统一格式的文件:属性文件、yaml、csv、json、xml等;
  • 文本文件:没有固定格式的文本文件。
    分类的目的是为了更好的识别文件中的字符串常量,充分利用字符串常量的上下文关联,以便在分析中最大程度的减少误报。

输入文件转换

  • 程序语言:通过各语言的语法解析器,解析成抽象语法树,提取语法树中的等于字符串的常量,以及对应的变量名;
  • 有统一格式的文件:照格式转换成变量名和字符串常量值;
  • 文本文件:采用token的方式分割成一个个的token,变成一个各的字符串常量。

密码密钥检查

在我们得到大量的变量名和字符串常量后,主要通过正则表达式匹配的方式完成目标的筛选。我们收集的密钥格式包括国内国外主流平台、SDK等相关系统的密钥配置参数名称,可以根据客户的实际情况手动增加和优化规则,进而提高检测的速度和效率。
由于检查密码密钥的种类和类型不同,可以通过配置文件来提高检查能力的可扩展性。
检查配置选项主要包括以下内容:

|信息 |描述 |
|检查类型 |可分为:变量名、字符串常量、或两个都检查 |
|密码密钥的类型 |用于区分不同类型的密码密钥;同时用于告警时区分具体检测到的密码密钥类型 |
|正则表达式 |主要设定匹配的长度,每个字符的可选类型 |
|熵值的阈值 |用于精确的识别密码密钥的类型,降低误报 |

例如:
检查硬编码的口令:检查变量名中包含:password、passwd、pwd的变量,且变量等于字符串常量;正则表达式可以设置成为:”.*(password|passwd|pwd)$”。
检查GitHub的个人凭证:检查字符串常量;这个凭证是以”ghp_”开头的,跟随长度为36的字符串,且每个字符可以为数字和字母;正则表达式可以设置成为:“ghp_[0-9a-zA-Z]{36}”。

密码密钥过滤

静态分析能很大程度上减少了人工审核的工作量,但由于检查模式的不确定性,也会带来不少的误报。误报会给用户在审核过程中带来很大的负面情绪,从而不愿继续使用工具。为了进一步降低误报,我们可以通过下面的方式来降低误报:

  • 密码密钥熵值的计算
    前面讨论过密码密钥的特点,可以通过检测密码密钥的信息熵的方式来降低误报。有些密码密钥设定了最低的阈值,但还是有很多密码密钥并未给出具体的阈值,这个就需要通过经验积累来设定,目前业界也有通过机器学习来完善这个阈值的设定。
  • 污点分析
    在代码中,对于口令的变量的取名上,很多并不会遵守可读性和可维护性来设定变量名,通过前面正则表达式的方式来查找硬编码密码的方式,会造成很多的漏报。这里还可以通过污点分析的方法,来推导出密码是否采用了硬编码。例如检查jdbc连接的密码参数,查看该参数是否为字符串常量。

报告输出

将经过过滤后的结果,输出告警,给出可能泄露的文件名和变量或可能为密码密钥的常量字符串位置,便于人工的排查。

硬编码检测实战

对于审计人员或者安全管理人员并不需要关注密钥检查的算法,通常只需要选用合适的流程,匹配恰当的工具,迅速准确的定位硬编码即可。

检查的流程

信息收集:此阶段中,审计人员进行必要的信息收集,包括本次审计要求、稳定版本的源代码。
工具审计:确定工具审计的标准和各项配置参数,使用Fortify、gitleaks等工具检测目标源代码,对工具检测的结果进行人工核查,筛选,分析,汇总。
人工审计:对客户要求人工审计的重点代码采用人工分析的方法,对源代码中的硬编码进行审计。
综合汇总:将工具审计和人工审计的结果进行对比汇总
输出报告:此阶段中,审计人员根据测试的结果编写直观的硬编码审计服务报告。
源代码硬编码审计要求工作人员有丰富的经验及新颖的思路,同时也是一项比较耗费时间和资源的工作,从效率考虑,一般选择性的抽取部分重要环节的代码进行人工审计。

开源检查工具

SecretScanner

项目地址:https://github.com/deepfence/SecretScanner
SecretScanner可以扫描主机上的容器镜像或本地目录,主要通过正则匹配,扫描效率较高,不仅支持代码而且支持容器扫描,并将结果输出到JSON文件,其中会包含SecretScanner找到的所有敏感数据的详细信息。
SecretScanner方便安全人员在主机上收集敏感数据,如果需要审计特定类型代码或者增加新的硬编码检测点,需要手动修改规则,默认配置文件位于项目中的config.yaml,可以根据实际情况自行修改。

image

不足:规则数量一般

gitleaks

Gitleaks为你提供了一种方法来扫描你的git存储库,以查找这些不需要的数据,这些数据应该是私有的,扫描可以自动化,以完全适合CI/CD工作流程,以便在密码识别更深入到代码库之前进行识别。
gitleaks比较有趣的有以下几点:

  • 支持私有存储库扫描以及需要基于密钥的身份验证的存储库。
  • 支持Gitlab批量组织和存储库所有者(用户)存储库扫描,并提取请求扫描以在常见CI工作流中使用。
  • 支持Pre-Commit,方便研发人员在提交之前进行硬编码检测,防止敏感信息提交。
  • 支持使用git log命令验证由gitleaks找到的发现

image

不足:对普通用户友好程度一般,对于pre-commit需要手动配置,并且在大文件扫描效率较低,官方已移除多线程配置参数,使用默认线程数进行扫描,如果在使用过程中感觉缓慢,可自行设定编译。同样规则数量一般,可以根据实际情况添加。

SASTs

各种SAST工具都可以检测硬编码,但是受限于使用场景,只能检测较少的硬编码场景,比如password,key等,对于特定的硬编码需要手动增加规则,规则友好性一般。

硬编码检测规则

无论gitleaks还是secretScanner,均使用正则匹配相关硬编码,敏感信息,我这里整理出部分敏感信息参数,供大家参考,至于规则,按照要求,自行补充即可,比较简单,这里就不单独描述了

1
regex: '(?i)((access_key|access_token|admin_pass|admin_user|algolia_admin_key|algolia_api_key|alias_pass|alicloud_access_key|amazon_secret_access_key|amazonaws|ansible_vault_password|aos_key|api_key|api_key_secret|api_key_sid|api_secret|api.googlemaps AIza|apidocs|apikey|apiSecret|app_debug|app_id|app_key|app_log_level|app_secret|appkey|appkeysecret|application_key|appsecret|appspot|auth_token|authorizationToken|authsecret|aws_access|aws_access_key_id|aws_bucket|aws_key|aws_secret|aws_secret_key|aws_token|AWSSecretKey|b2_app_key|bashrc password|bintray_apikey|bintray_gpg_password|bintray_key|bintraykey|bluemix_api_key|bluemix_pass|browserstack_access_key|bucket_password|bucketeer_aws_access_key_id|bucketeer_aws_secret_access_key|built_branch_deploy_key|bx_password|cache_driver|cache_s3_secret_key|cattle_access_key|cattle_secret_key|certificate_password|ci_deploy_password|client_secret|client_zpk_secret_key|clojars_password|cloud_api_key|cloud_watch_aws_access_key|cloudant_password|cloudflare_api_key|cloudflare_auth_key|cloudinary_api_secret|cloudinary_name|codecov_token|config|conn.login|connectionstring|consumer_key|consumer_secret|credentials|cypress_record_key|database_password|database_schema_test|datadog_api_key|datadog_app_key|db_password|db_server|db_username|dbpasswd|dbpassword|dbuser|deploy_password|digitalocean_ssh_key_body|digitalocean_ssh_key_ids|docker_hub_password|docker_key|docker_pass|docker_passwd|docker_password|dockerhub_password|dockerhubpassword|dot-files|dotfiles|droplet_travis_password|dynamoaccesskeyid|dynamosecretaccesskey|elastica_host|elastica_port|elasticsearch_password|encryption_key|encryption_password|env.heroku_api_key|env.sonatype_password|eureka.awssecretkey)[a-z0-9_ .\-,]{0,25})(=|>|:=|\|\|:|<=|=>|:).{0,5}[\"]([0-9a-zA-Z\-_=]{8,64})[\"]' 

参考

密码密钥硬编码检查
SecretScanner
gitleaks
浅谈密钥硬编码