本文目录导读:

- 核心概念:什么是“敏感数据”?
- 静态代码扫描(白盒发现)—— 最主流方法
- 动态扫描(黑盒发现)—— 找运行时的输出
- 日志与流量审计(被动发现)
- 针对 PHP 特有的高级发现技巧
- 具体操作流程(SOP 参考)
- 总结推荐组合打法
PHP 敏感数据发现通常指在代码中扫描、识别和定位可能泄露敏感信息的代码片段,常见于安全审计、等保测评、DevSecOps 流程中。
这里分几个层面给出具体的发现方法和工具链:
核心概念:什么是“敏感数据”?
在 PHP 代码中,我们主要关注四类:
- 硬编码凭据:数据库密码、API Key、支付宝/微信支付密钥、SMTP 密码、JWT Secret。
- 个人信息(PII):身份证号、手机号、银行卡号、家庭住址、健康记录。
- 敏感配置:云存储 Bucket 链接、服务器内网 IP、调试开关(如
APP_DEBUG=true、display_errors=On)。 - 内部逻辑泄露:目录遍历路径、备份文件(
.sql、.bak)、phpinfo()或var_dump()输出未做访问控制。
静态代码扫描(白盒发现)—— 最主流方法
这是最常用的方式,不需要运行代码,直接扫描源码。
专业商业工具
- Fortify SCA(Micro Focus):静态应用安全测试(SAST)界的标杆,能识别复杂数据流(如
$_POST输入直接拼入mysql_query),规则库庞大,误报率相对低。 - Checkmarx:擅长从“输入点”追踪到“敏感操作点”的污点追踪(Taint Analysis),能发现“数据写到日志”这类问题。
- SonarQube(社区版/开发者版):扫描规则里内置了“硬编码密码(Hardcoded Credentials)”和“加密算法不牢靠”等规则,免费版足够用。
开源免费工具(推荐使用)
| 工具名称 | 主要用途 | 特点 |
|---|---|---|
| Semgrep | 通用代码安全扫描 | 强力推荐,基于规则模式匹配,速度快,规则可自定义(.yml),社区有现成的 PHP 敏感数据规则包。 |
| PHPStan / Psalm | 静态类型分析 | 主要查逻辑错误,但可以通过自定义扩展(Stubs)来追踪敏感变量的流向。 |
| RIPS(已停维护) | 曾经的 PHP 扫描王者 | 代码可用,但只支持 PHP 5.x 时代,现在不建议在新项目用。 |
| gitleaks / trufflehog | 专门扫密钥/令牌 | 不一定只针对 PHP,但它能通过正则和熵检测(Entropy),直接在 Git 历史里找出暴露的密码和 Token。 |
手动正则/命令行发现(轻量快速排查)
在服务器或 CI 环境直接运行 grep:
# 1. 查找硬编码密码(常见赋值模式)
grep -rnE "\b(password|passwd|pwd|secret)\b\s*=" --include="*.php" /var/www/html/
# 2. 查找数据库连接(常见函数)
grep -rn "mysql_connect\|mysqli_connect\|new PDO" --include="*.php" /var/www/html/
# 3. 查找常见第三方API密钥(阿里云/腾讯云/AWS)
grep -rnE "AKIA[0-9A-Z]{16}|LTAI[0-9A-Z]{12,}" --include="*.php" /
# 4. 查找开启的调试和报错(潜在信息泄露)
grep -rn "display_errors\s*=\s*On\|APP_DEBUG\s*=\s*true" --include="*.php" /var/www/html/
动态扫描(黑盒发现)—— 找运行时的输出
静态扫描找“代码里的问题”,动态扫描找“跑起来后泄露了什么”。
- 接口响应抓取:
- 使用 Burp Suite 或抓包工具。
- 重点查看
/api/v1/user/detail或admin.php等接口。 - 关注点:JSON 响应中是否多于客户端需要的字段(比如返回了
password_hash、id_card、phone)。
- 错误页探测:
- 篡改请求参数使其报错(例如发送 100 万个字符到
int字段)。 - 如果开启了
display_errors,PHP 会把绝对路径、数据库连接参数、甚至 SQL 片段打出来。
- 篡改请求参数使其报错(例如发送 100 万个字符到
- 文件泄露探测:
- 尝试访问
/.git/、/.env、/config/database.php.bak、/.sql备份文件。 - 发送
?debug=1参数看是否会触发var_dump()。
- 尝试访问
日志与流量审计(被动发现)
- 数据库日志:查看 MySQL 慢查询日志或常规
log,看是否记录了完整的 SQL(里面可能包含未脱敏的手机号)。 - Web 访问日志:检查
access.log中是否有人通过?user=13800000000这种 GET 方式传参(URL 里的 PII 比 POST 更危险,因为会进入日志)。 - 框架日志:Laravel 的
storage/logs/中,排查异常抛出时是否将$request->all()或$password原样写入。
针对 PHP 特有的高级发现技巧
这一条建议单独看,PHP 环境有特定的泄露路径:
.git泄露:如果项目根目录有.git文件夹且被 web 服务器解析,可以用GitHack或git-dumper直接恢复源码,密码可能藏在历史提交里。phpinfo()探测:如果代码里有phpinfo()且未做 IP 限制,会直接把$_SERVER、$_ENV和加载的扩展路径全打出来(包含数据库主机名、源代码路径)。extract()与变量覆盖:如果代码用了extract($_GET),攻击者可能通过构造参数覆盖掉硬编码的$password变量。- 反序列化漏洞:对
unserialize()的入参未过滤,可能导致在__destruct()方法中输出敏感类属性。
具体操作流程(SOP 参考)
如果你需要写一个“敏感数据发现”的脚本,建议按此顺序:
// 伪代码逻辑示意
$scanDir = '/var/www/html';
$rules = [
'sql_password' => '/("password"|db_pass)\s*=>\s*["\']([^"\']+)["\']/i',
'api_key' => '/(sk-live-|pk-live-|AKIA|AIza[0-9A-Za-z_-])/',
'手机号' => '/(1[3-9]\d{9})/', // 注意国内手机号正则
];
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($scanDir));
foreach ($iterator as $file) {
if ($file->getExtension() !== 'php') continue;
$content = file_get_contents($file->getPathname());
foreach ($rules as $type => $pattern) {
if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
echo "[高危] 文件: {$file->getPathname()} 发现类型: {$type} 位置: 第" . $line . "行\n";
}
}
}
总结推荐组合打法
| 场景 | 推荐工具 | 目的 |
|---|---|---|
| 上线前 CI 集成 | Semgrep + GitLeaks | 阻断硬编码密码提交,发现代码中的敏感调用。 |
| 存量大项目审计 | SonarQube 或 Fortify | 生成完整的漏洞报告,人工核查。 |
| 应急排查 | grep 正则在服务器直接搜 |
快速定位某个测试密钥或硬编码密码的分布。 |
| 运行期防泄露 | Burp Suite / 清理日志 | 确保响应体不携带额外 PII,日志不落明文。 |
注意:扫描出来的高概率“敏感信息”需要人工复核,很多账号密码可能是测试数据或加密后的密文,需人工判断是否为真实生产密钥,若发现敏感数据,建议配合 git-filter-repo 清理 Git 历史,并强制轮换现有密钥。