协同共建|深入分析应用密钥安全治理之术

时间:2024-10-26 19:29:37

文|应用漏洞扫描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 敏感密钥概况 

场景一、匹配密钥本身的特定格式

密钥本身有很强的特定格式,可以直接用正则匹配。例如,

  1. 腾讯云appid会以AKID开头,例如:AKIDxxxx
  2. Github服务间相互调用的密钥以ghs开头,例如:ghs_tVGHE4***666222

匹配方式简单直白 —— 写正则匹配密钥本身。例如,针对在代码中硬编码腾讯云密钥的风险,使用开源静态代码检查工具semgrep,不难快速编写出扫描规则:

  1. pattern-regex: (AKID)(?i)[a-zA-Z0-9]{32}\b
  2. pattern-not-regex: q-ak.+(AKID)(?i)[a-zA-Z0-9]{32}

场景二、匹配密钥及所处的代码结构

现实场景中我们会发现,绝大部分密钥本身没有很强的特征,例如:大部分数据库的账密可以是任意ascii可见字符,但随代码硬编码却比较常见。

以数据库账密泄露排查为例,根据经验,我们得出三类大规模匹配检索思路:

1)匹配含密钥的数据源声明链接

  1. Go语言中的DNS链接,如:redis://:@127.0.0.1:6380/test?is_proxy=true
  2. 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. // 列举几种典型的前缀授权场景:
  3. // 1、允许访问所有对象:"*"
  4. // 2、允许访问指定的对象:"a/", "b/"
  5. // 3、允许访问指定前缀的对象:"a*", "a/*", "b/*"
  6. // 如果填写了“*”,将允许用户访问所有资源;除非业务需要,否则请按照最小权限原则授予用户相应的访问权限范围。
  7. ("allowPrefixes", new String[] {
  8.        "exampleobject",
  9.        "exampleobject2"
  10. });
  11. // 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
  12. // 简单上传、表单上传和分块上传需要以下的权限,其他权限列表请看 /document/product/436/31923
  13. String[] allowActions = new String[] {
  14.            // 简单上传
  15.        "name/cos:PutObject",
  16.        // 表单上传、小程序上传
  17.        "name/cos:PostObject",
  18.        // 分块上传
  19.        "name/cos:InitiateMultipartUpload",
  20.        "name/cos:ListMultipartUploads",
  21.        "name/cos:ListParts",
  22.        "name/cos:UploadPart",
  23.        "name/cos:CompleteMultipartUpload"
  24. };
  25. ("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和纵深防御体系的探索实践。同时,也致力于与公司内部其他安全团队一起协同建设安全能力及系统,并将相关经验与业界交流分享。