moh0206/healthvault-api-secure-refactor
GitHub: moh0206/healthvault-api-secure-refactor
对一个故意存在漏洞的 PHP 医疗档案 API 进行安全审计并重构,覆盖漏洞识别、利用链分析与结构性修复的全过程。
Stars: 0 | Forks: 0
# HealthVault-API — 安全审计与安全重构
对一个故意设计为存在漏洞的 PHP 健康档案 API 进行安全审计与加固。
从源码到漏洞利用链,追踪了三个源文件中的七个缺陷,在解释器边界进行了重构,
并使用自动化 PHPUnit 套件进行了验证。
此为马来西亚理工大学 (Universiti Teknologi Malaysia,2026) **SECR4483 安全编程** 的课程作业。
## 场景
该演练模拟了医疗档案系统的入侵事件:患者数据被窃取并出售,员工凭证在离线状态下被破解,伪造的临床医生账户批准了受控药物的发放,以及数据库中的药物剂量被篡改。
工作包含三个部分——识别每个漏洞在运行时*为何*可被利用,重构代码使得漏洞利用链从结构上被切断(而非依赖模式匹配),并通过测试和实际 payload 证明修复有效。
## 漏洞清单
| # | 文件 | 漏洞 | CWE |
|---|------|---------------|-----|
| A | `search.php` | SQL 注入 — 用户输入被拼接到查询字符串中 | CWE-89 |
| B | `search.php` | 反射型 XSS — 关键词未经编码直接输出到 HTML | CWE-79 |
| C | `search.php` | 反射型 XSS — “无记录”分支中存在相同漏洞 | CWE-79 |
| D | `auth.php` | 对字节长度 (`strlen`) 而非字符长度进行边界检查 | CWE-1284 |
| E | `auth.php` | MD5 凭证哈希 — 速度快、无盐、可逆 | CWE-916 / CWE-327 |
| F | `crypto_vault.php` | AES-128-ECB — 确定性加密,泄露明文模式 | CWE-327 |
| G | `crypto_vault.php` | 硬编码加密密钥 | CWE-321 / CWE-798 |
## 漏洞利用链
此次入侵并非七个独立的 bug——而是一条链,每个漏洞都促成了下一个漏洞的发生。值得注意的是,`auth.php` 本身并不包含注入漏洞;它的防护失败是因为 `search.php` 泄露了凭证存储。
| 阶段 | 漏洞 | 级联结果 |
|-------|------|------------------|
| 1 | A — SQL 注入 | 攻击者输入重写了查询结构;实现跨 schema 的任意读取 |
| 2 | A + root DB 权限 | 完整数据库转储,包含 `staff_credentials` |
| 3 | E — MD5 哈希 | 转储的摘要以 GPU 速度在离线状态下被破解;恢复出明文员工密钥 |
| 4 | 恢复的凭证 | 幽灵“医生”账户通过身份验证并批准发药 |
| 5 | F + G — ECB + 硬编码密钥 | “加密”的记录可被轻易解密并泄露模式 |
| 6 | A (写入路径) | 注入升级为 `UPDATE` — 临床记录被篡改 |
## 重构:前后对比
**SQL 注入 (漏洞 A)** — 旧版查询以一个扁平字符串的形式到达数据库,因此解析器无法区分攻击者输入和命令。重构后通过独立通道发送模板和参数,并设置 `EMULATE_PREPARES=false` 强制执行真正的服务端预处理,确保没有任何内容在客户端被拼接组装。
```
// Legacy — input compiled as part of the command
$sql = "SELECT id, name, illness_history FROM patient_records
WHERE name LIKE '%" . $keyword . "%'";
// Refactored — input bound as data only
$stmt = $pdo->prepare('SELECT id, name, illness_history FROM patient_records
WHERE name LIKE :kw');
$stmt->execute(['%' . $keyword . '%']);
```
**反射型 XSS (漏洞 B, C)** — 旧版 `echo` 不区分上下文,因此 `` | 被解析为活动标记 | 作为 HTML 实体返回,处于非活动状态 |
| 翻转的密文字节 | 被静默接受 (ECB 没有完整性检查) | GCM 标签验证失败 → HTTP 422 |
```
./vendor/bin/phpunit --testdox
```
## 设计原理
黑名单或 WAF 方法只能匹配已知的恶意字符串,并且可以通过新颖的编码来绕过。而参数化和输出编码改变了*允许输入流向何处* — 在结构上将其限制在 SQL 的数据平面,或者在 HTML 中将其呈现为惰性文本。这里没有任何东西可以被绕过,因为 payload 永远不会到达会执行它的解释器。
审计还将每次故障映射到马来西亚的 **《2010 年个人数据保护法》**,主要是第 9 条安全原则,涵盖了健康记录的未经授权披露和未经授权修改。
## 项目结构
```
config/ Application and database configuration
legacy/ Original vulnerable implementation, retained for comparison
public/ Web-accessible entry point
sql/ Database schema and seed data
src/ Refactored, hardened application code
tests/ PHPUnit suite (Authenticator, CryptoVault)
```
## 本地运行
**环境要求:** PHP 8.3+, Composer, MySQL/MariaDB
```
git clone https://github.com/moh0206/healthvault-api-secure-refactor.git
cd healthvault-api-secure-refactor
composer install
cp .env.example .env # add DB credentials and a 32-byte encryption key
mysql -u -p < sql/schema.sql
php -S localhost:8000 -t public
```
测试端点:
```
# 患者搜索
curl "http://localhost:8000/search.php?keyword="
# 加密 / 解密记录
curl -i -X POST "http://localhost:8000/crypto_vault.php" \
--data-urlencode "action=encrypt" \
--data-urlencode "payload="
```
## ⚠️ 注意
这是学术课程作业。`legacy/` 中的代码是**故意设计为有漏洞的**,绝不能被部署、重用或暴露在任何网络中。重构后的版本展示了在教学模式下的安全编码实践,尚未经过生产环境使用审计。
## 参考
修复遵循了 OWASP 的 SQL 注入防御、跨站脚本防御和密码存储速查表;NIST SP 800-38D (GCM/GMAC);以及 MITRE CWE 对每个已识别漏洞的定义。
**作者:** Mohamed Ali Mohamed Ali Abouelela
网络与安全工程,马来西亚理工大学
标签:ffuf, OpenVAS, PHP, Web安全, 安全测试工具, 教学项目, 服务器监控, 漏洞修复, 网络安全培训, 蓝队分析