文|应用漏洞扫描Oteam、PCG安全团队
Martinzhou
I. 背景
当前,各类开放平台、依托云原生技术模式的PaaS/SaaS服务涌现,密钥(Credentials / Secrets)成了跨服务、跨平台调用认证的关键一环。短短几十个字符,背后往往通向上万台CVM或者整个业务数据。好似露出洋面的一角冰山,稍往下深挖,底下可能就是牵动企业安全命脉的“金山银山”。
当前,针对个人用户登录密码的保护技术日臻成熟,基于短信验证码、指纹、人脸或是物理设备的多因子验证获得较为广泛的落地。但用于服务与服务间认证的密钥,往往未获得同等力度的安全保护。
针对上述与应用密钥有关的风险,本文将从研发及数据安全视角,结合近期实践,从四方面探讨可采取的治理思路,以求抛砖引玉,也欢迎大家参与共建,共同探索。阅读本文,你将收获:
有哪些存在风险的敏感密钥?
如何检测敏感密钥泄露?
开发人员如何安全地使用密钥?
业务系统如何安全地设计密钥?
II. 密钥安全四问
2.1 有哪些存在风险的敏感密钥?
知己知彼、百战不殆。在着手扫描清查密钥风险前,可以先对业界各类常见的密钥类型做梳理。否则容易陷入疲于应对已暴露密钥,case by case添加规则。基于我们的实践,分享可参考分类思路如下:
值得一提的是,各企业内部可能也建设了一系列PaaS、SaaS平台,可根据自身实际情况酌情梳理、纳入风险评估。
接着,就需要评估已梳理各类密钥关联的风险(如,会导致数据被匿名访问)及检测特征(如,密钥自身存在的显著特征,详参见2.2.1部分讨论)。梳理思路可以表格形式记录,参考如下:
2.2 如何检测敏感密钥泄露?
2.2.1 敏感密钥概况
场景一、匹配密钥本身的特定格式
密钥本身有很强的特定格式,可以直接用正则匹配。例如,
-
腾讯云appid会以AKID开头,例如:AKIDxxxx
-
Github服务间相互调用的密钥以ghs开头,例如:ghs_tVGHE4***666222
匹配方式简单直白 —— 写正则匹配密钥本身。例如,针对在代码中硬编码腾讯云密钥的风险,使用开源静态代码检查工具semgrep,不难快速编写出扫描规则:
-
pattern-regex: (AKID)(?i)[a-zA-Z0-9]{32}\b
-
pattern-not-regex: q-ak.+(AKID)(?i)[a-zA-Z0-9]{32}
场景二、匹配密钥及所处的代码结构
现实场景中我们会发现,绝大部分密钥本身没有很强的特征,例如:大部分数据库的账密可以是任意ascii可见字符,但随代码硬编码却比较常见。
以数据库账密泄露排查为例,根据经验,我们得出三类大规模匹配检索思路:
1)匹配含密钥的数据源声明链接
-
Go语言中的DNS链接,如:redis://:@127.0.0.1:6380/test?is_proxy=true
-
Java JDBC链接,如:jdbc:mysql://127.0.0.1:3306/db_xxx?user=&password=&useUnicode=true&characterEncoding=UTF8
2)与密钥关联的特性API/域名、IP+端口特征
内部以名字服务形式声明的数据库地址,如:db.foo.com:3306
3)代码结构中有明确的sink点特征,通过追踪回溯source点,可判断变量值是否是字符串或常量等硬编码形式。例如,Python声明mysql连接的方法
db = (host='localhost', user='testuser', password='test123', database='TESTDB')
场景三、密钥的模糊泛匹配
除上述两类有较明显规律可循的场景外,不少硬编码密钥的代码结构更为松散,这时候还可以尝试编写一些模糊泛匹配规则,以增强清查效果。
根据前期实践经验,敏感密钥通常会分布于三类场景中:
各类源代码文件,如:go、java等
配置文件,如:yaml、toml等
其他类文件,如:markdown、csv、Dockerfile等
编写敏感密钥的规则时,同样拆分成三大部分:
键(Key),通常匹配是否包含特定关键词,如token、psw、password等;
分隔符,通常匹配不同编程语言中的分隔符,如:=>、:、=等;
值(Value),通常匹配特定的密钥字符集范围及长度
2.2.2 检测手段及工具
结合上述对密钥的分析,我们将可以选用的手段及工具概括如下:
业界可供参考的敏感密钥扫描工具分类推荐如下:
1)trufflehog
项目地址:
/trufflesecurity/truffleHog
检测思路:取长度大于20的魔术字符串,再匹配规则
支持范围:多语言
2)whispers
项目地址:
/Skyscanner/whispers
检测思路:正则
支持范围:多语言
3)credential-digger
项目地址:
/SAP/credential-digger
检测思路:机器学习
支持范围:多语言
4)hardcores
项目地址:
/s0md3v/hardcodes
检测思路:正则
支持范围:多语言
5)gitleaks
项目地址:
/zricethezav/gitleaks
检测思路:正则
支持范围:多语言
2.2.3 DevOps流程嵌入
基于对历史安全事件的复盘,我们发现密钥有可能在研发流程的任一环节泄漏并产生风险。因此,比较合适采用“洋葱式”层层检查设防、运营收敛:
密钥异常行为检测
除在软件研发生命周期的各环节匹配检测各类密钥外,还可以结合密钥的HTTP访问日志建立调用请求行为的基线特征,识别异常的密钥调用。
例如:如果发现某类云平台的密钥调用来源IP的城市分布超过特定数值,则可以推测可能被错误地硬编码在APP/小程序客户端对外发布,存在安全风险。
2.3 开发人员如何安全地使用密钥?
2.3.1 综述
简单来说,安全地在应用中使用密钥有两大原则:一是安全地使用密钥;二是当生产环境应用代码中涉及多个密钥时,应考虑使用KMS类方案集中托管并定期轮换。
其中,安全使用密钥遵循以下原则:
1)权限最小化,缩小关联的权限、缩短有效期、专钥专用,例如:部分云平台提供的STS密钥(Security Token Service)
2)为密钥设置IP/PMS调用白名单
3)为密钥传输启用TLS,防止中间人劫持窃取
4)密钥定期轮换并保持资产负责人准确
5)使用密钥托管平台托管系统(KMS)
下面将以腾讯业务生态下两种最常见的密钥为例,介绍相关安全开发方式。
2.3.2 安全开发示例分析
* 以下仅为举例说明,非真实案例。
1)微信开放平台密钥
最常见风险场景是,为了实现用户微信登录的功能,将微信开放平台的secret直接硬编码在APP、HTML5页面及小程序等源代码可被直接接触的代码环境中。
在此场景下,安全的开发方式为:
1、客户端仅携带appid,由应用在服务器侧保存appsecret;
2、应用发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到我们自己的第三方应用,并且带上授权临时票据code参数;
3、服务器将appid + appsecret + 临时票据code发往微信开放平台,换取当前用户的access_token;
4、微信开放平台返回access_token给服务器,该凭据应由第三方应用服务器代用户保管与当前用户的登录态绑定。
值得一提的是,如果开发者已经使用了上述不安全的编码方式,或应用下架密钥已无需使用,应及时登录微信开放平台重置并替换或注销密钥。
* 以下仅为举例说明,非真实案例。
2)腾讯云密钥
最常见风险场景是,为了在小程序、客户端中调用云产品的功能,开发者直接将常规云密钥AK/SK硬编码在产品中。此类密钥往往绑定的不止一类云资源,甚至关联了CVM、CLB等关键资产。云场景下两类常见密钥的区别概括如下:
其常见的风险是误将高权限的常规密钥,包含在APP、网页或小程序中,可被外部攻击者窃取。
安全的开发方式,以在前端或小程序向COS存储桶直传文件开发场景为例,可概述为:创建STS临时密钥(临时访问凭证)。其核心在于:1⃣️限制可操作的对象;2⃣️限制对存储桶允许执行的操作
-
// 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径
-
// 列举几种典型的前缀授权场景:
-
// 1、允许访问所有对象:"*"
-
// 2、允许访问指定的对象:"a/", "b/"
-
// 3、允许访问指定前缀的对象:"a*", "a/*", "b/*"
-
// 如果填写了“*”,将允许用户访问所有资源;除非业务需要,否则请按照最小权限原则授予用户相应的访问权限范围。
-
("allowPrefixes", new String[] {
-
"exampleobject",
-
"exampleobject2"
-
});
-
// 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
-
// 简单上传、表单上传和分块上传需要以下的权限,其他权限列表请看 /document/product/436/31923
-
String[] allowActions = new String[] {
-
// 简单上传
-
"name/cos:PutObject",
-
// 表单上传、小程序上传
-
"name/cos:PostObject",
-
// 分块上传
-
"name/cos:InitiateMultipartUpload",
-
"name/cos:ListMultipartUploads",
-
"name/cos:ListParts",
-
"name/cos:UploadPart",
-
"name/cos:CompleteMultipartUpload"
-
};
-
("allowActions", allowActions);
2.4 平台系统如何安全地设计密钥?
如果你正运营着开放平台,自身就是密钥的提供方,则需要注意以下几点:
1)密钥的appid或secret在格式上必须具备明显、可区分的特征
2)遵循“权限最小化”原则,平台应为开发者提供密钥安全加固功能。包括但不限于:允许开发者重置密钥、为密钥设置IP调用来源、允许设置密钥关联的接口权限等
3)除明文账密外,为服务端应用密钥启用引入MFA机制
2.4.1 密钥自身的设计
2021年Github工程团队在其技术博客中宣布重构其开放API关联的密钥结构。由于旧密钥仅为一串特定长度的字母数字组合,容易与其他哈希值混淆产生误报,增加了密钥扫描的难度。
通过引入gh等一系列前缀,按功能从密钥本身设计区分出不同功能场景,使安全扫描排查更简单。其差异对比如下:
2.4.2 密钥管理功能设计
同时,如果平台系统提供的API要带密钥,应至少具备以下安全能力,供开发者自行配置使用:
1)允许设置调用来源IP白名单
2)调用频控限制
3)禁用或重置密钥能力
2.4.3 提供MFA机制
除上述围绕密钥自身安全设计、密钥安全加固功能外,也可以考虑为服务端应用密钥引入MFA机制。业界也不乏已落地机制参考,例如:MySQL企业版提供的LDAP、FIDO认证插件,参见《LDAP Pluggable Authentication》,/doc/refman/8.0/en/
III. 总结
密钥虽不起眼,但应用背后的应用资产和数据的安全往往悬于短短几十个字符间。本文尝试从密钥使用和提供两方视角入手,探讨各环节和场景下的密钥安全综合治理。整体可概括为:
平台系统作为密钥提供方
1)密钥的appid或secret在格式上必须具备明显、可区分的特征
2)平台为开发者提供了密钥调用提供加固机制,包括但不限于:允许开发者重置密钥、为密钥设置IP调用来源等
平台系统作为密钥使用方
1)已引入安全工具对扫描代码仓库/制品/Web接口等渠道,进行密钥泄露检查
2)已遵循第三方开放平台提供的最佳安全实践在系统中使用密钥;或使用配置中心或KMS密钥托管系统,单独存储密钥并定期轮换
过程中诸多事宜乍看轻松,实践中却会有诸多考量点,应用漏洞扫描Oteam、PCG安全团队、腾讯安全朱雀实验室等团队也在持续探索密钥安全治理的落地方案,欢迎大家留言交流。
# 参考资料
[1] Access Keys Will Kill You: Before You Kill The Password,
/docs/us-16/materials/
[2] Behind GitHub’s new authentication token formats
/2021-04-05-behind-githubs-new-authentication-token-formats/
[3] Detecting Credential Compromise in AWS,
/us-18/Wed-August-8/
# 团队介绍
应用漏洞扫描Oteam,由公司各BG安全团队联合组成,专注于构建自动化安全系统为腾讯业务保驾护航。目前,运营有Web漏洞、高危服务、代码漏洞、客户端安全、云PaaS服务及制品相关的系列大型自动化安全系统。
PCG技术安全团队,隶属于平台与内容事业群的技术平台线,主要负责为事业群下属社交、视频、信息流服务等重点业务产品提供一对一的应用及数据安全保障,以及DevSecOps和纵深防御体系的探索实践。同时,也致力于与公司内部其他安全团队一起协同建设安全能力及系统,并将相关经验与业界交流分享。