3、硬编码凭据的风险:源代码中的密码、配置文件中的密钥、环境变量中的Token

说实话,硬编码凭据这个问题,我在工厂里见过太多次了。每次做安全审计,翻开代码仓库或者配置文件,总能找到几个“惊喜”。

什么叫硬编码凭据?说白了,就是把密码、密钥、Token 这些敏感信息,直接写在源代码里、配置文件里,或者环境变量里。听起来很蠢对吧?但现实中,这几乎是每个工厂 IT/OT 项目都踩过的坑。

我自己就经历过一次。那是一个 MES 系统的升级项目,前任开发把数据库连接密码直接写在 Java 代码里。结果代码被一个实习生不小心 push 到了公开仓库。嗯,那天晚上我们整个团队都在改密码。

3.1 源代码中的密码:最直接的“裸奔”

先看源代码。这是最危险的一种硬编码方式。密码就躺在代码里,谁都能看到。

典型场景:

  • 数据库连接字符串里直接写用户名和密码
  • 调用第三方 API 时,在代码里写死 API Key
  • PLC 或 SCADA 系统的通信密码硬编码在脚本中

举个例子,你可能会看到这样的代码:

// 千万别这么干!
String dbUrl = "jdbc:mysql://192.168.1.100:3306/mes_db";
String dbUser = "admin";
String dbPassword = "P@ssw0rd123!";

Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);

为什么说这很危险?你想想看,代码要经过版本控制吧?Git、SVN 这些工具会记录每一次变更。密码一旦提交上去,就永远留在历史记录里了。哪怕你后来删掉了,有心人翻翻历史记录就能找到。

我在一个汽车零部件工厂就遇到过这种事。他们的 PLC 上位机软件里硬编码了 OPC 服务器的连接密码。后来有员工离职,带走了源代码。结果怎么样?整个车间的生产数据被人看了个遍。

⚠️ 核心风险:

  • 版本控制历史会永久保留密码
  • 代码审查时,所有能看到代码的人都能获取密码
  • 一旦泄露,需要修改所有相关系统的密码,工作量巨大

3.2 配置文件中的密钥:看似隐蔽,实则脆弱

有些人觉得,把密码写在配置文件里总比写在代码里强吧?嗯,确实好一点,但也好不到哪去。

配置文件通常和代码一起打包、一起部署。常见的配置文件有 .properties、.yaml、.json、.xml 这些格式。密码就明晃晃地写在里面。

# application.properties
spring.datasource.url=jdbc:mysql://192.168.1.100:3306/mes_db
spring.datasource.username=admin
spring.datasource.password=P@ssw0rd123!

你可能会说:“我把配置文件放到 .gitignore 里不就行了?” 好问题。但现实是,很多人忘了加。或者项目初期没加,等想起来的时候,密码已经提交上去了。

还有一种情况更常见——配置文件被直接放在服务器上,权限设置不当。我记得有一次去一家化工厂做评估,他们的 SCADA 系统配置文件居然对 everyone 可读。我随手打开一看,里面全是 PLC 的通信密码和数据库连接串。

配置文件类型 常见泄露途径 风险等级
.properties 版本控制、服务器权限 高
.yaml 版本控制、日志输出 高
.xml 版本控制、备份文件 高
.env 开发环境泄露、容器镜像 极高

💡 我的建议:

配置文件里的密钥,至少要做到:

  • 使用占位符,比如 ${DB_PASSWORD}
  • 实际值通过外部方式注入,比如环境变量或密钥管理服务
  • 配置文件本身设置严格的文件系统权限

3.3 环境变量中的Token:进步了,但还不够

环境变量比前两种方式进步了不少。至少密码不会直接出现在代码和配置文件里了。很多团队觉得:“我把 Token 放在环境变量里,总安全了吧?”

嗯,比硬编码好,但也不是万能的。

环境变量的问题在哪?我总结了几点:

  • 进程继承问题:子进程会继承父进程的环境变量。如果你在 shell 里 export 了一个 Token,然后启动了一个子进程,这个子进程也能看到 Token。
  • 日志泄露:很多应用会在启动时打印环境变量用于调试。我曾经见过一个日志文件,里面清清楚楚地写着 API_TOKEN=sk-xxxxx。
  • 容器镜像:Docker 容器里设置的环境变量,会被打包进镜像层。别人 pull 了你的镜像,就能通过 docker inspect 看到。
  • 调试工具:ps aux 命令在某些配置下会显示进程的环境变量。
# 不推荐的做法
export API_TOKEN="sk-abcdef123456"
python app.py

# 更好的做法
# 使用密钥管理服务,比如 HashiCorp Vault 或 AWS Secrets Manager
# 应用启动时从密钥服务获取 Token

⚠️ 特别注意:

在 OT 环境中,很多老旧设备不支持环境变量注入。这时候怎么办?我见过有人把 Token 写在启动脚本里,结果脚本又被提交到版本控制里了。嗯,绕了一圈又回来了。

3.4 一张图看懂硬编码凭据的风险路径

下面这张图,是我根据实际项目经验画的。它展示了硬编码凭据从产生到泄露的完整路径。

硬编码凭据风险路径图 硬编码凭据风险路径图 源代码中的密码 直接写在代码里 配置文件中的密钥 随代码一起分发 环境变量中的Token 进程继承与日志泄露 版本控制泄露 Git/SVN历史记录 服务器权限不当 文件/目录权限 日志与调试信息 启动日志/ps命令 严重后果 数据泄露 · 系统被控 · 生产中断 合规处罚 · 声誉损失 💡 核心观点: 硬编码凭据的三个来源,最终都会通过版本控制、权限漏洞或日志泄露等途径 暴露给攻击者。唯一的解法是:从源头杜绝硬编码,使用密钥管理服务。

3.5 为什么工厂环境尤其危险?

你可能觉得,上面说的这些在 IT 系统里也常见啊。没错,但在工厂 OT 环境里,问题更严重。

原因有三:

  • 设备生命周期长:工厂里的 PLC、SCADA 系统,一用就是十年八年。代码和配置写死了密码,后面想改?难。很多设备甚至不支持远程修改密码。
  • 供应商代码质量参差不齐:我见过太多设备供应商的代码,密码直接硬编码。你跟他说要改,他说“我们一直这么写的”。嗯,很无奈。
  • OT 网络隔离的假象:很多人觉得 OT 网络是隔离的,密码泄露了也没事。但现在的工厂,IT/OT 融合是大趋势。一旦攻击者从 IT 侧打进 OT 网络,硬编码的密码就是他们的“万能钥匙”。

📌 总结一下:

硬编码凭据不是“会不会出事”的问题,而是“什么时候出事”的问题。源代码、配置文件、环境变量,这三个地方是重灾区。我的建议很简单:

  1. 代码里绝对不出现真实密码
  2. 配置文件使用占位符,实际值外部注入
  3. 环境变量作为过渡方案,最终迁移到密钥管理服务

别等到出了事再改。那时候,代价就大了。