carlmasters02/python-static-analyzer
GitHub: carlmasters02/python-static-analyzer
一个基于 AST 解析与污点跟踪的教学型 Python 静态分析工具,用于在不执行代码的情况下检测 SQL 注入和命令注入漏洞。
Stars: 0 | Forks: 0
# pytaint
[](https://github.com/carlmasters02/python-static-analyzer/actions/workflows/ci.yml)
[](https://codecov.io/gh/carlmasters02/python-static-analyzer)
[](LICENSE)
`pytaint` 将 Python 文件解析为 AST,识别不可信的 *sources* 和危险的
*sinks*,并使用简单的污点跟踪来标记那些在没有经过净化(sanitization)的情
况下从 source 流向 sink 的数据。它会报告文件名、行号,以及每个发现结果可
被利用的 *原因*。该项目作为一个学习项目增量构建而成——每个模块都有大量注
释,并且每个核心安全概念都在代码中进行了内联解释。
## 演示
```
$ pytaint samples/
samples/vuln_command_injection.py:15 [command-injection]
untrusted data (variable 'host') reaches os.system() — os.system() passes its string argument to the system shell
samples/vuln_sql_injection.py:12 [sql-injection]
untrusted data (variable 'username') reaches cursor.execute() — a DB cursor .execute() runs its string as SQL
...
4 finding(s) across 2 file(s) scanned
```
## 安装
需要 Python 3.9+。除标准库外无其他依赖。
### 选项 A — 使用 Python (pip)
```
git clone https://github.com/carlmasters02/python-static-analyzer.git
cd python-static-analyzer
pip install . # installs the `pytaint` command
pytaint samples/
```
### 选项 B — 使用 Docker(无需 Python)
```
git clone https://github.com/carlmasters02/python-static-analyzer.git
cd python-static-analyzer
docker build -t pytaint .
docker run --rm -v "$PWD/samples":/scan pytaint /scan
```
## 用法
```
pytaint PATH [--format {text,json}] [--no-taint-params] [--no-color] [--version]
PATH file or directory to scan (recursive)
--format output format: text (default) or json
--no-taint-params don't treat function parameters as tainted
(higher precision, lower recall)
--no-color disable ANSI colors in text output
--version show version and exit
```
当干净时退出代码为 `0`,存在发现结果时为 `1`(适用于 CI),路径错误时为
`2`。你也可以将其作为模块运行:`python -m pytaint samples/`。
## 工作原理 — 四个核心概念
**AST (抽象语法树)。** 纯文本形式的源代码很难进行分析(`grep` 无法区分代码
和注释)。Python 内置的 `ast` 模块将文本解析为反映代码结构的 *树*,因此
`os.system(user_input)` 会变成一个我们可以精确检查的 `Call` 节点。参见
[src/pytaint/parser.py](src/pytaint/parser.py)。
**Sources 和 sinks。** **Source** 是不可信数据进入程序的地方——例如
`input()`、`request.args`、`sys.argv`,以及(假设的)函数参数。**Sink** 是
危险的操作——例如 `os.system()` / `subprocess.*(shell=True)`(→ 命令注入)
和 `cursor.execute()`(→ SQL 注入)。知识库位于
[src/pytaint/signatures.py](src/pytaint/signatures.py)。
**污点分析。** 我们将来自 source 的数据标记为“已污染”(tainted),并在赋值
和字符串操作(`+`、f-strings、`.format()`、`%`)中对其进行追踪。如果被污染
的数据到达了 sink,并且没有被 sanitizer(`shlex.quote`、`int(...)` 或 SQL
参数化)清理掉,我们就会将其报告出来。这正是让该工具变得 *精确*
的原因,而不是盲目地标记每一个 `os.system` 调用。参见
[src/pytaint/taint.py](src/pytaint/taint.py)。
**精确率与召回率。** *精确率* = 在我们标记出的结果中,有多少是真实的?
*召回率* = 在真实的漏洞中,我们发现了多少个?它们之间存在权衡;而 F1 分数
则平衡了这两者。根据下方的带标签基准测试进行了测量。
```
source file ──ast.parse──► AST ──find sources & sinks──► taint engine
(introduce → propagate → check sinks w/ sanitizers, shell=, SQL params)
──► findings ──► text / JSON report
```
## 基准测试结果(精确率 / 召回率)
基于 [benchmark/cases/](benchmark/cases/) 进行评分——包含 22 个带标签的测
试用例,涵盖了真实的漏洞、安全的“相似代码”(参数化 SQL、`shell=False`、已
净化的输入),以及特意用来测试引擎已知盲点的用例。可以使用
`python -m benchmark.score` 复现该结果。
| 模式 | TP | FP | FN | TN | 精确率 | 召回率 | F1 |
|------|---:|---:|---:|---:|----------:|-------:|---:|
| default(参数被污染) | 9 | 1 | 3 | 9 | 90% | 75% | 82% |
| `--no-taint-params` | 4 | 1 | 8 | 9 | 80% | 33% | 47% |
客观地来看:那 **1 个误报** 是因为在到达 sink 之前,变量被重新赋值为了一
个常量(我们在同一个作用域内对流不敏感)。那 **3 个漏报** 是我们遗漏的真实
漏洞——一个未建模的 source(`os.getenv`)、通过返回值传递的污点(没有过程
间分析),以及通过列表元素传递的污点。第二行以数字形式展示了精确率与召回率
之间的权衡。
## 真实世界扫描
针对五个真实的 GitHub 仓库(275 个 `.py` 文件)运行了扫描;完整的分类见
[REAL_WORLD_SCAN.md](REAL_WORLD_SCAN.md)。重点:在 253 个成熟的库代码文件
(flask / requests / httpie)中实现了 **零误报**;在故意存在漏洞的 `dvpwa`
中发现了 **一个正确的真实漏洞**;而在 `DSVW` 中则有一整个应用规模的 **遗漏
漏洞**,因为它的输入 source(`self.path`)不在我们的签名列表中——这最清楚
地表明,*一个污点分析工具的效果仅取决于它的 source/sink 列表的质量*。
## 开发
```
pip install -e ".[dev]" # install with test tools (pytest, coverage)
pytest # run the suite + coverage report
python -m benchmark.score # run the precision/recall benchmark
python explore_ast.py --scan samples/vuln_sql_injection.py # teaching tool
```
每次推送到 `main` 分支时,都会在 GitHub Actions 上跨 Python 3.9、3.11 和
3.13 运行测试套件。测试覆盖率会报告给 Codecov。
## 已知局限性(出于设计考量——这是一个教学工具)
- **绑定签名:** 仅能捕获在 [signatures.py](src/pytaint/signatures.py) 中定义的
sources/sinks;缺失的 source 会导致隐性的漏报。
- **仅限过程内分析:** 对于流入辅助函数并从该函数内的 sink 流出的污点,不会
跨越调用边界进行追踪。
- **作用域内流不敏感:** 忽略语句的顺序。
- **无容器/属性追踪:** 不会追踪通过 list/dict 元素或对象属性传递的污点。
## 项目结构
```
python-static-analyzer/
├── src/pytaint/ # the package
│ ├── __init__.py # version
│ ├── parser.py # AST parsing + call discovery
│ ├── signatures.py # source/sink knowledge base
│ ├── detector.py # classify calls as source/sink
│ ├── taint.py # the taint engine
│ ├── scanner.py # directory walk + robustness
│ ├── reporting.py # text / JSON output
│ ├── cli.py # the `pytaint` command
│ └── __main__.py # enables `python -m pytaint`
├── tests/ # pytest / unittest suite
├── samples/ # labeled vulnerable/safe demo files
├── benchmark/ # labeled cases + precision/recall scorer
├── explore_ast.py # teaching CLI (AST dump / classify / scan)
├── REAL_WORLD_SCAN.md # results on real repos
├── .github/workflows/ci.yml
├── Dockerfile
├── pyproject.toml
├── LICENSE # MIT
└── README.md
```
## 许可证
基于 [MIT 许可证](LICENSE) 发布——可自由使用、修改和分发;只需保留
版权声明即可。
标签:CISA项目, Python, SQL注入检测, 代码安全审计, 命令注入检测, 安全规则引擎, 无后门, 自动化payload嵌入, 请求拦截, 逆向工具, 错误基检测, 静态代码分析