ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

代码安全学习手记(一):基于 GitLab CI 与 Semgrep 搭建本地 SAST 自动化扫描流水线

代码安全学习手记(一):基于 GitLab CI 与 Semgrep 搭建本地 SAST 自动化扫描流水线 代码安全学习手记一基于 GitLab CI 与 Semgrep 搭建本地 SAST 自动化扫描流水线写在前面最近在研究 DevSecOps开发安全运维的相关概念。以前写代码总觉得“能跑通就行”但了解了安全攻防后才发现代码里的硬编码 Token、拼接 SQL、未授权访问等漏洞很多时候在开发阶段就能被拦截。为了搞清楚SAST静态应用程序安全测试是如何在实际开发流程中发挥作用的我决定在本地用 Docker 搭建一套 GitLab GitLab Runner 环境并引入开源扫描工具Semgrep跑通一条自动化的代码安全扫描 Pipeline。这篇文章记录了我折腾全过程的技术细节、规则编写心得与踩坑调优经验01. 深入理解 SAST工作原理与选型对比在动手配置前我先理清了 SAST 的核心工作机制与逻辑。1.1 SAST 是如何“看懂”代码的普通的字符串匹配如grep只能做最简单的正则查找极易产生误报。现代 SAST 工具的内部分析通常分为三个递进层次抽象语法树AST, Abstract Syntax Tree把源代码解析成树状结构忽略空格、换行和注释只关注代码的语法结构。控制流分析Control Flow Analysis分析代码执行的路径分支If/Else、Loop、Function Call构建控制流图CFG。污点分析Taint Analysis / 数据流追踪这是检测注入类漏洞如 SQL 注入、XSS、命令注入的核心它将外部可控输入标为Source污染源将敏感函数或数据库执行点标为Sink汇聚点。只要追踪到一条未经过过滤清洗Sanitizer的路径从 Source 传到了 Sink就会报出高危漏洞。1.2 主流 SAST 工具横向对比在工具选型上我对比了几款目前主流的开源与商用方案维度SonarQubeBanditCheckovSemgrep(本篇选择)主打语言多语言 (Java/C#/JS等)Python 专属IaC (Terraform/K8s)多语言 (Python/Java/Go/JS/C等)规则编写难度极高 (需编写 Java 插件)中等 (需编写 Python AST)中等 (Python/YAML)极低 (模式匹配语法类似写普通代码)架构与部署重型 (需独立 Web Server/DB)轻量 CLI轻量 CLI极轻量 (OCaml 引擎可直接 Docker/CLI 运行)扫描性能较慢 (适合夜间全量构建)快很快极快 (非常适合提交级 CI/CD 增量扫描)终上所述Semgrep的语法极其直观且支持直接通过定义 YAML 规则实现自定义语义匹配非常适合嵌入到 CI/CD 管道中实现快速反馈。02. 本地实验环境搭建GitLab Docker Runner为了模拟真实的 Enterprise DevSecOps 场景我使用 Docker Compose 在本地拉起了一个完整的 GitLab 实例和配套的 GitLab Runner。2.1 Docker Compose 部署配置在本地新建docker-compose.ymlversion:3.8services:gitlab:image:gitlab/gitlab-ce:16.8.0-ce.0container_name:local-gitlabrestart:alwayshostname:gitlab.localenvironment:GITLAB_OMNIBUS_CONFIG:|external_url http://localhost:8080 gitlab_rails[gitlab_shell_ssh_port] 2222ports:-8080:8080-2222:22volumes:-./gitlab/config:/etc/gitlab-./gitlab/logs:/var/log/gitlab-./gitlab/data:/var/opt/gitlabhealthcheck:test:[CMD,curl,-f,http://localhost:8080/-/health]interval:30stimeout:10sretries:5gitlab-runner:image:gitlab/gitlab-runner:latestcontainer_name:local-gitlab-runnerrestart:alwaysdepends_on:-gitlabvolumes:-/var/run/docker.sock:/var/run/docker.sock-./gitlab-runner/config:/etc/gitlab-runner2.2 注册并配置 Docker Runner启动容器后docker compose up -d等待 GitLab 初始化完毕。登录 GitLab 后进入Admin Area - CI/CD - Runners获取 Runner Registration Token然后在 Runner 容器内完成交互式/命令行注册dockerexec-itlocal-gitlab-runner gitlab-runner register\--non-interactive\--urlhttp://gitlab:8080/\--registration-tokenGR1348941YOUR_TOKEN_HERE\--executordocker\--docker-imagedocker:24.0.5\--descriptionsast-dedicated-runner\--tag-listsast,docker\--docker-volumes/var/run/docker.sock:/var/run/docker.sock\--docker-network-modehost03. 编写 Semgrep 自定义安全扫描规则Semgrep 官方提供了非常丰富的 Rule Registry如p/owasp-top-10、p/security-audit但商业落地中最核心的价值在于针对公司内部框架/业务逻辑开发自定义规则。我在项目根目录下创建了.semgrep/rules.yml编写了涵盖硬编码密钥、SQL 注入与命令注入的规则rules:# 规则 1硬编码 API Key / Secret 阻断规则-id:detect-hardcoded-secretlanguages:[python,javascript,typescript,yaml]severity:ERRORmessage:【高危】检测到源码中存在硬编码的敏感密钥 (%s)请立即移除并使用环境变量或 KMS 统一加载。metadata:cwe:CWE-798: Use of Hard-coded Credentialsowasp:A07:2021 - Identification and Authentication Failurespatterns:-pattern-either:-pattern:$KEY sk-...-pattern:$KEY AKIA...-pattern:const $KEY sk-...-pattern:$KEY bearer...# 规则 2Python Flask 中的 SQL 拼接注入检测 (数据流/模式匹配)-id:python-sql-injection-concatlanguages:[python]severity:ERRORmessage:【高危】发现潜在的 SQL 注入风险发现直接在 execute() 中使用字符串格式化/拼接。请改用 ORM 或预编译参数语句。metadata:cwe:CWE-89: Improper Neutralization of Special Elements used in an SQL Commandowasp:A03:2021 - Injectionpatterns:-pattern-either:-pattern:$CURSOR.execute(fSELECT...{$VAR}...)-pattern:$CURSOR.execute(...%s... % ($VAR,...))-pattern:$CURSOR.execute(... $VAR ...)# 规则 3命令注入风险分析 (subporcess / os.system 滥用)-id:dangerous-subprocess-shell-truelanguages:[python]severity:WARNINGmessage:【中危】检测到 subprocess 调用中设置了 shellTrue 且包含可变参数可能引发系统命令注入。patterns:-pattern:subprocess.$FUNC(...,shellTrue,...)-pattern-not:subprocess.$FUNC(...,shellTrue,...)# 过滤纯静态字符串命令04. 编写.gitlab-ci.yml实现自动化安全卡点接着配置.gitlab-ci.yml。为了避免代码扫描阻断正常紧急 Pipeline 体验通常采用“分级卡点”策略对ERROR级别的严重漏洞强行打断对WARNING级别记录 Report 但软放行。stages:-test-security_sastvariables:SEMGREP_SEND_METRICS:off# 阶段一基础测试unit_tests:stage:testscript:-echo Running application unit tests...# 阶段二Semgrep SAST 安全卡点semgrep_sast_scan:stage:security_sasttags:-sast# 路由到具有 Docker 能力的专属 Runnerimage:returntocorp/semgrep:latestscript:-echo -echo 开始执行 Semgrep SAST 代码安全扫描 -echo # 1. 扫描并输出标准 JSON 报告供后续分析/平台对接-semgrep scan--config.semgrep/rules.yml--configp/owasp-top-10--json-o semgrep-sast-report.json||true# 2. 控制台可读输出如果匹配到 severityERROR 的规则命令将返回 exit code 1-semgrep scan--config.semgrep/rules.yml--configp/owasp-top-10--errorartifacts:name:sast-report-${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHA:0:8}when:alwayspaths:-semgrep-sast-report.jsonexpire_in:7 days# 核心卡点逻辑false 意味着一旦有 ERROR 级别的漏洞直接把 Pipeline 置为 Failedallow_failure:false05. 效果验证与踩坑调优经验为了验证效果我在测试项目中提交了一段带有硬编码 Token和SQL 注入漏洞的 Flask 测试代码# app.pyimportsqlite3importsubprocessfromflaskimportFlask,request appFlask(__name__)# 漏洞点 1硬编码 API KeyAPI_SECRET_KEYsk-1234567890abcdef1234567890abcdefapp.route(/user)defget_user():user_idrequest.args.get(id)connsqlite3.connect(test.db)cursorconn.cursor()# 漏洞点 2SQL 拼接注入queryfSELECT * FROM users WHERE id {user_id}cursor.execute(query)returnstr(cursor.fetchall())app.route(/ping)defping_host():targetrequest.args.get(target)# 漏洞点 3命令注入风险subprocess.run(fping -c 1{target},shellTrue)returnOK[图片 3 描述GitLab CI 控制台显示 Semgrep 安全扫描卡点失败终端日志示意图]绘制建议画一个 Linux Terminal 控制台样式。顶部显示 CI 执行环境与脚本调用命令中间高亮打印出 Semgrep 规则匹配结果使用红色加粗框框出detect-hardcoded-secret与python-sql-injection-concat命中的具体代码行号及修复建议底部用醒目的红字打印出ERROR: Semgrep found 2 ERROR-level issues. Exiting with code 1与ERROR: Job failed: exit code 1标识构建流水线被卡点成功阻断。运行结果与踩坑调优总结推送代码后GitLab CI 成功拦截了构建输出了清晰的报告以下是在本地调优过程中总结的三个实战经验1. 解决 Docker-in-Docker (DinD) 卷挂载路径找不到问题踩坑刚开始 Runner 跑 Semgrep 容器时报No such file or directory错误。原因这是因为用宿主机的docker.sock运行 sibling 容器时Semgrep 容器寻找的挂载路径是宿主机路径而非 Runner 容器内的路径。解法在注册 Runner 时正确挂载目录或者直接使用包含 Semgrep 环境的独立 Runner 镜像。2. 规则调优如何控制“误报率False Positives”SAST 推行最大的阻力往往是开发人员抱怨“误报太多”。优化策略对于测试代码如tests/、test_*.py或自动生成代码如 Protobuf 生成文件应该在规则或命令中使用--exclude彻底排除避免干扰主流程。semgrep scan--config.semgrep/rules.yml--excludetests/--exclude*_test.go3. 避免 CI/CD 全量扫描导致的超时优化策略全量扫描项目在大型代码库中极其耗时。Semgrep 支持对 Git 变更进行增量扫描仅扫描当前 MR 相比于主干修改的文件# 增量扫描仅对比 git diff 修改的文件semgrep scan--config.semgrep/rules.yml --baseline-commit HEAD~106. 学习小结通过这次项目折腾我对 SAST 和 DevSecOps 的落地有了更深入的体会安全左移Shift Left绝不仅仅是加个扫描工具关键在于 feedback 机制要足够快、足够准能帮开发者在 IDE 内部或代码提交瞬间发现问题。规则治理是 DevSecOps 研发的核心直接使用全量开源规则池不仅容易慢还会被海量误报淹没。必须根据企业自身的编码规范对规则进行版本化管理与精准裁剪。
返回列表