饮墨

子安饮墨馀三斗,留与卿儿作赋来

Semgrep 代码安全扫描实战:3 步将 SAST 嵌入 CI/CD Pipeline,上线前拦截高危漏洞

1 views

痛点:代码安全漏洞总在上线后才被发现

团队辛辛苦苦做了 Code Review、写了单元测试,上线后安全扫描一跑——SQL 注入、硬编码密钥、SSRF、路径穿越,一堆高危漏洞排着队等你修。

传统 SAST(Static Application Security Testing)工具如 Fortify、Checkmarx,动辄几十万年授权费,扫描慢、误报多、规则配置像在写论文。小团队根本用不起,大团队用了也怨声载道。免费的 linter 工具只管代码风格,对安全漏洞视而不见。

结果就是:安全扫描要么不做,要么在上线后才补救——而这时修复成本已经翻了 10 倍。

方案:Semgrep — 轻量、快速、开源的代码安全扫描引擎

Semgrep 是一款开源静态分析工具,核心定位是"开发者友好的 SAST"。它的规则用类似源代码的模式匹配语法编写,不需要编译项目、不依赖语言特定的构建工具,扫描速度以秒计。

与传统方案的关键差异:

对比维度 传统 SAST(Fortify 等) Semgrep OSS
成本 数十万/年 开源免费
扫描速度 分钟~小时级 秒级(增量扫描更快)
规则编写 专有 DSL,学习成本高 类代码模式匹配,5 分钟上手
语言支持 广泛但重 30+ 语言(Python/Go/Java/JS/TS/Rust 等)
CI 集成 复杂配置 一行命令搞定
误报率 偏高 低(模式匹配精确)

Semgrep 的核心思路是代码模式匹配——你用目标语言的语法描述"危险代码长什么样",Semgrep 就帮你在整个代码库中找出来。不需要理解完整编译语义,所以又快又准。

实操步骤

Step 1:安装并运行第一次扫描

# 安装(pip / brew / docker 均可)
pip install semgrep

# 用官方规则集扫描当前项目
semgrep scan --config auto .

--config auto 会自动拉取 Semgrep Registry 中按语言匹配的安全规则集(覆盖 OWASP Top 10、CWE 常见漏洞等 2000+ 规则)。

扫描结果示例:

app/views.py
  python.django.security.sql-injection-extra-where
    Detected SQL injection risk: raw string in extra() where clause
    Severity: ERROR

    42│   results = User.objects.extra(where=[f"name = '{user_input}'"])

每个发现带规则 ID、CWE 编号、严重级别和修复建议链接,直接告诉你问题在哪、怎么修。

Step 2:编写团队专属规则

官方规则覆盖通用安全问题,但每个团队都有自己的安全规范。Semgrep 的规则语法直接使用目标语言的代码模式,上手极快。

示例 1:禁止使用 os.system()(防命令注入)

# .semgrep/no-os-system.yml
rules:
  - id: ban-os-system
    patterns:
      - pattern: os.system(...)
    message: |
      禁止使用 os.system(),存在命令注入风险。
      请用 subprocess.run(..., shell=False) 替代。
    severity: ERROR
    languages: [python]
    metadata:
      cwe: ["CWE-78: OS Command Injection"]

示例 2:检测硬编码 AWS 密钥

rules:
  - id: hardcoded-aws-access-key
    pattern-regex: (?i)AKIA[0-9A-Z]{16}
    message: |
      检测到疑似硬编码 AWS Access Key,请使用环境变量或 Secrets Manager。
    severity: ERROR
    languages: [generic]

示例 3:禁止 Go 代码中跳过 TLS 验证

rules:
  - id: go-insecure-tls-skip-verify
    patterns:
      - pattern: |
          &tls.Config{..., InsecureSkipVerify: true, ...}
    message: |
      生产代码禁止跳过 TLS 证书验证(InsecureSkipVerify: true)。
    severity: ERROR
    languages: [go]

运行自定义规则:

semgrep scan --config .semgrep/ .

规则文件放在 .semgrep/ 目录下随代码仓库一起版本管理,团队成员共享同一套安全标准。

Step 3:集成到 CI/CD Pipeline

GitHub Actions 集成:

# .github/workflows/semgrep.yml
name: Semgrep Security Scan
on:
  pull_request: {}
  push:
    branches: [main]

jobs:
  semgrep:
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep
    steps:
      - uses: actions/checkout@v4
      - name: Run Semgrep
        run: |
          semgrep scan \
            --config auto \
            --config .semgrep/ \
            --error \
            --sarif -o semgrep.sarif \
            .
      - name: Upload SARIF
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: semgrep.sarif

GitLab CI 集成:

semgrep:
  image: semgrep/semgrep
  stage: test
  script:
    - semgrep scan --config auto --config .semgrep/ --error --json -o gl-sast-report.json .
  artifacts:
    reports:
      sast: gl-sast-report.json
    when: always

关键参数:

  • --error:发现 ERROR 级别问题时返回非零退出码,阻断 Pipeline
  • --sarif / --json:标准格式输出,GitHub Security Tab / GitLab Security Dashboard 直接展示
  • 同时加载 --config auto(官方规则)和 --config .semgrep/(团队规则)

落地策略建议: PR 阶段用 --diff-depth 2 只扫增量变更文件,速度更快;main 分支推送做全量扫描确保无遗漏。新规则先设 severity: WARNING 观察一周,误报可控后再升级为 ERROR 阻断。

避坑指南

坑 1:规则太多导致噪音爆炸

--config auto 包含 2000+ 规则,刚引入时可能一次报出几百个 finding,团队直接劝退。

解法: 初次落地只开 --severity ERROR,先消灭高危漏洞。低危问题留待后续迭代,或用 .semgrepignore 排除误报密集的规则:

semgrep scan --config auto --severity ERROR .

坑 2:测试代码触发大量误报

测试文件中的 mock 数据、fixture 经常触发"硬编码密钥""SQL 拼接"等误报。

解法: 在 .semgrepignore 中排除测试目录:

# .semgrepignore
tests/
*_test.py
*_test.go
testdata/
fixtures/

坑 3:只在 CI 扫,开发者本地无感知

开发者 push 后 CI 扫描不过,来回改提交浪费时间。

解法: 搭配 pre-commit hook,git commit 时本地即时拦截:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/semgrep/semgrep
    rev: v1.90.0
    hooks:
      - id: semgrep
        args: ['--config', 'auto', '--config', '.semgrep/', '--error']
pip install pre-commit && pre-commit install

反馈链路从"CI 失败 → 查日志 → 改代码 → 重新 push"缩短为本地秒级修复。

总结

Semgrep 把 SAST 从"昂贵的安全团队专属工具"变成了"每个工程师都能用的代码安全 linter":

  1. 5 分钟上手:pip install semgrep && semgrep scan --config auto .,零配置完成第一次扫描
  2. 规则即代码:用目标语言的模式写安全规则,团队共享版本管理,不需要学专有 DSL
  3. CI 原生:一个容器镜像 + 几行 YAML,Pipeline 中自动阻断高危漏洞
  4. 渐进式落地:先 ERROR only → 再补 WARNING → 最后加 pre-commit,团队不会被误报淹没

如果你的项目还没有 SAST 扫描,先跑一次 semgrep scan --config auto .,看看代码里藏着多少"惊喜"——早发现一个漏洞,就少一次半夜被叫起来修 hotfix。

分享:

相关文章