cognis-digital/sqlsec
GitHub: cognis-digital/sqlsec
一款防御性 SQL 注入安全 linter 和交互式训练器,通过扫描源码中不安全的 SQL 构建模式并教授参数化查询来帮助开发者预防注入漏洞。
Stars: 0 | Forks: 0
# sqlsec
**一款防御性的 SQL 安全 linter 和训练器。** `sqlsec` 会扫描您的 SQL 字符串
和源文件,查找会导致 SQL injection 的构建模式,并且
内置了一个小型的交互式训练器,用于教授参数化查询安全。
它是**仅用于教育和防御目的的**。它不会执行攻击、连接
到任何数据库,也不会运行它检查的 SQL —— 它只读取源文本并报告。
通俗来讲:当输入作为*结构*的一部分被拼接到查询中,
而不是作为*数据*传递时,就会发生 SQL injection。`sqlsec` 会寻找这些
拼接方式(string concatenation、f-string、`%`-formatting、`.format()`、动态
`EXEC`、stacked statements 等),并指出问题所在,同时提供安全的重写方案。
- 维护者:**Cognis Digital**
- 许可证:**COCL 1.0**
- Python 3.10+,**仅使用标准库**(无第三方运行时依赖)
## 安装
```
pip install -e .
# 或者,使用 test extra:
pip install -e ".[dev]"
```
这会安装 `sqlsec` 控制台命令。
## 用法
### 检查源码中不安全的 SQL 构建方式
```
sqlsec lint path/to/file.py
sqlsec lint path/to/project/ # recurses; scans .py and .sql
sqlsec lint examples/vulnerable.py # the bundled bad sample
sqlsec lint examples/safe.py # the bundled clean sample (no findings)
```
常用 flag:
| Flag | 效果 |
| --- | --- |
| `--json` | 以 JSON 格式输出发现的问题(用于 CI / 工具)。 |
| `--fail-on ` | 如果有任何发现的问题达到或超过此严重级别(`info`/`low`/`medium`/`high`/`critical`),则以非零状态退出。可用作 CI 门禁。 |
| `--select SQL001,SQL004` | 仅运行指定的规则。 |
| `-v` / `--verbose` | 打印额外的提示。 |
表格输出示例:
```
SEVERITY RULE LOCATION MESSAGE
-------- ------ -------------------------- -----------------------------------------
HIGH SQL001 examples/vulnerable.py:16:13 SQL query built by string concatenation...
CRITICAL SQL005 examples/vulnerable.py:46:5 Dynamic execution of a concatenated SQL...
```
退出代码:`0` = 正常 / 门禁未触发,`1` = 达到 `--fail-on` 阈值,
`2` = 错误的调用(缺少路径、未知规则)。
### 解释规则
```
sqlsec explain # list every rule
sqlsec explain SQL002 # what it catches + the safe pattern
```
### 训练
一个基于精心编写的 SQL injection 和参数化查询课程库生成的交互式多选题测验。
```
sqlsec train --list # list lesson topics (non-interactive)
sqlsec train --topic basics # quiz one topic
sqlsec train --topic all # quiz everything
```
对于每个问题,输入选项编号,或输入 `q` 退出。
## 规则集
`sqlsec` 内置了一套精心编写的规则集(无复制内容)。每条规则都会在
不良样本上触发,并在安全的等效代码上保持静默。
| ID | 严重级别 | 捕获内容 |
| --- | --- | --- |
| SQL001 | high | 通过 string concatenation (`"..." + var`) 构建的 SQL |
| SQL002 | high | 插入到 SQL 中的 f-string 插值 |
| SQL003 | high | 通过 printf 风格的 `%`-formatting 构建 SQL |
| SQL004 | high | 使用 `str.format()` 构建 SQL |
| SQL005 | critical | 对组装的 SQL 执行动态 `EXEC` / `sp_executesql` / `EXECUTE IMMEDIATE` |
| SQL006 | medium | 单个字符串中的 stacked / 多语句查询 |
| SQL007 | high | 嵌入 SQL 中的恒真(tautology)条件 |
| SQL008 | medium | 查询字面量内部的 SQL 注释序列(`--`、`/*`、`#`) |
| SQL009 | medium | 使用预构建字符串且无参数调用 `execute()` |
| SQL010 | low | 通过拼接构建的 `LIKE` 模式 |
| SQL011 | medium | 插入到 SQL 中的表/列 identifier |
| SQL012 | low | 使用组装/动态文本的 `executescript()` |
linter 在匹配之前会剥离 Python 的 `#` 注释(字符串外部的),因此
关于 SQL 的注释不会被标记 —— 除非规则刻意针对*字符串字面量内部*的
注释序列。
## 安全模式,简而言之
保持代码中的 SQL 文本固定不变;将值作为绑定参数单独传递:
```
# unsafe
cur.execute("SELECT * FROM users WHERE id = " + user_id)
cur.execute(f"SELECT * FROM users WHERE id = {user_id}")
# safe
cur.execute("SELECT * FROM users WHERE id = ?", (user_id,)) # sqlite3 / qmark
cur.execute("SELECT * FROM users WHERE id = %s", (user_id,)) # many drivers
```
占位符绑定的是**值**,而不是 identifier。对于从输入中选择的表/列名,
在它接触到 SQL 之前,请通过固定的白名单进行映射。
## 示例
- `examples/vulnerable.py` — 故意设为不安全;涵盖了每一条 Python 规则。
- `examples/safe.py` — 对应的参数化实现;检查通过,无安全问题。
```
sqlsec lint examples/vulnerable.py # many findings
sqlsec lint examples/safe.py # none
```
## 开发
```
pip install -e ".[dev]"
python -m pytest # on Windows: set PYTHONUTF8=1
```
测试会验证每条规则是否在不良样本上触发,并在安全样本上保持静默,
检查严重级别和 `--fail-on` 门禁的退出代码,并验证课程库
是否加载成功,以及测验循环是否能在没有真实 stdin 的情况下运行。
## 适用范围和局限性
`sqlsec` 是一个启发式的 linter,而不是一个 parser。它经过调整,可以标记常见的、
危险的构建模式,并且在符合惯用写法的安全代码上产生的误报很少。
它不会捕获所有可能的不安全查询,且干净的运行结果也不能证明是
安全的 —— 请将其视为与代码审查、默认使用参数化查询、
最小权限的 DB 账户以及输入验证并存的一层防护。
许可证:COCL 1.0。
标签:Python, 代码安全, 代码规范工具, 代码质量检查, 安全培训, 无后门, 漏洞枚举, 静态代码扫描