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, 代码安全, 代码规范工具, 代码质量检查, 安全培训, 无后门, 漏洞枚举, 静态代码扫描