代码扫描
要查找自己代码中的漏洞,你可以使用静态应用程序安全测试(SAST)。SAST被认为是白盒测试,因为它可以完全访问源代码。它并不只是纯粹的静态代码分析(通常包括构建软件),但与动态应用程序安全测试(DAST)不同,它不是在运行时执行,而是在编译时执行。代码扫描在GitHub中实现了SAST。
GitHub 中的代码扫描
在GitHub中,SAST被称为代码扫描,它适用于所有公共仓库以及启用了GitHub高级安全的私有仓库。你可以使用支持静态分析结果交换格式(SARIF)的工具来进行代码扫描。SARIF是基于JSON的OASIS标准,用于定义静态分析工具的输出格式。GitHub代码扫描当前支持SARIF 2.1.0,这是该标准的最新版本(请见 [GitHub文档](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning))。因此,任何支持SARIF 2.1.0的工具都可以集成到代码扫描中。
运行你的代码扫描
代码扫描使用GitHub Actions执行分析。大多数代码扫描工具会自动将结果上传到GitHub——但如果你的代码扫描工具没有这样做,你可以使用以下Action上传任何SARIF文件:
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: results.sarif
该Action接受单个 .sarif
(或 .sarif.json
)文件,或者包含多个文件的文件夹。如果你的扫描工具不支持SARIF,但可以转换结果,你可以使用像ESLint这样的工具。你可以使用 @microsoft/eslint-formatter-sarif
将输出转换为SARIF格式并上传结果:
jobs:
build:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v2
- name: Run npm install
run: npm install
- name: Run ESLint
run: node_modules/.bin/eslint build docs lib script spec-main -f node_modules/@microsoft/eslint-formatter-sarif/sarif.js -o results.sarif || true
- uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: results.sarif
然而,大多数代码扫描工具会直接与GitHub集成。
入门
要开始使用代码扫描,请前往 Settings | Security & analysis | Code scanning | Set up 或 Security | Code scanning alerts。这两者都会带你到 /security/codescanning/setup
,这里会列出所有代码扫描选项。顶部是GitHub原生的代码扫描工具——CodeQL分析。GitHub会分析你的仓库,并显示它可以在市场中找到的所有适用于你仓库中检测到的语言的其他工具——例如42Crunch、Anchore、CxSAST、Veracode等。在本书中,我们将重点介绍CodeQL,但其他工具的集成方式相同。如果你点击 Set up this workflow,GitHub将为你创建一个工作流(见图14.14):

如果你已经设置了代码扫描,你可以通过点击 Add more scanning tools 来从结果页面添加其他工具(见图14.15):

该工作流模板有触发条件:push、pull_request 和 schedule。schedule触发条件可能会让你感到惊讶,但有一个简单的解释——可能会有新规则可以检测到你代码库中以前没有识别到的漏洞。所以,定期运行构建也是一个好主意。触发器每周在随机一天、随机时间运行一次。当然,GitHub不希望所有代码扫描同时运行。你可以根据需要调整schedule:
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '42 16 * * 2'
该工作流需要写入安全事件的权限:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
CodeQL支持以下语言:C++(cpp)、C#(csharp)、Go、Java、JavaScript、Python和Ruby。GitHub 会尝试检测仓库中使用的语言,并设置矩阵,使得每种语言可以独立构建。如果需要,可以添加其他语言:
strategy:
fail-fast: false
matrix:
language: [ 'csharp', 'javascript' ]
分析本身非常简单——检查仓库,初始化给定语言的分析,运行自动构建并执行分析:
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
Autobuild 步骤会尝试构建你的源代码。如果构建失败,你需要修改工作流并自行构建代码。有时,只需要设置正确的版本环境——例如 Node.js 或 .NET 的版本:
- name: Setup Node
uses: actions/setup-node@v2.5.0
with:
node-version: 10.16.3
通过这种方式,你可以方便地执行静态代码扫描,及时发现并修复潜在的安全漏洞。
代码扫描警报
你可以在每个仓库的 Settings | Security & analysis | Code scanning 中管理你的代码扫描警报,如图14.15所示。在组织级别,你可以查看所有仓库的概览,并可以跳转到每个仓库的具体结果页面(见图14.16):

你可以像管理问题一样对警报进行过滤、排序和搜索。
严重性
每个代码扫描警报都会被分配一个严重性级别。严重性是通过使用通用漏洞评分系统(CVSS)来计算的。CVSS是一个开放框架,用于传达软件漏洞的特征和严重性(有关更多信息,请参见GitHub Blog 2021)。严重性帮助你进行警报的优先级排序和分流。
在问题中跟踪警报
跟踪代码扫描警报的最佳方式是将其记录为一个问题。你可以通过点击警报中的 Create issue 按钮来创建一个问题(见图14.17):

但这只是打开一个新问题,并将警报的链接添加到Markdown任务列表中(见图14.18):

警报会显示出它正在被某个问题跟踪的指示,就像嵌套问题一样(见图14.19)。
数据流分析
在代码下方的区域,你可以看到警报的详细信息。CodeQL支持数据流分析,可以检测应用程序中由数据流动引起的问题。点击 Show paths,查看数据如何在应用程序中流动(见图14.19):

你可以追踪数据在应用程序中的流动过程。在这里的示例中,你可以看到数据被分配并传递的12个步骤,直到它被记录到日志中(见图14.20):

这就是CodeQL的真正强大之处。它不仅仅是对源代码的语义分析。
CodeQL查询
在代码扫描警报中,你可以找到检测到问题的查询的引用。点击 View source,查看GitHub上的查询(见图14.21):

这些查询是开源的,你可以在 [GitHub](https://github.com/github/codeql) 上找到它们。每种语言都有一个文件夹,在CodeQL文件夹中,你可以在 ql/src 下找到查询文件。这些查询的文件扩展名是 .ql
。
拉取请求集成
代码扫描与拉取请求(Pull Requests)集成得很好。代码扫描结果集成到拉取请求的检查中,详细页面会展示结果概览(见图14.23):

代码扫描还会在代码中添加警报的注释,您可以直接在此处对发现的问题进行分类,修改其状态为“假阳性”、“用于测试”或“不修复”(见图14.24):

您可以在 设置 | 安全与分析 | 代码扫描 中定义哪些警报严重性级别应该导致拉取请求因安全问题或其他发现而失败(见图14.25):

拉取请求集成有助于保持主分支的干净,并在合并之前检测到问题,将代码分析纳入审查过程。
代码扫描配置
配置代码扫描有许多选项。在工作流中的 init CodeQL
操作有一个名为 queries
的参数,您可以使用它来选择默认的查询套件之一:
-
security-extended: 包含比默认查询更低严重性的更多查询
-
security-and-quality: 包括
security-extended
中的查询,以及可维护性和可靠性查询
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
queries: security-and-quality
您还可以使用 queries
参数添加自定义查询。该参数接受本地路径或对其他仓库的引用,包括 Git 引用(分支、标签或 SHA)。添加加号可以在默认查询的基础上添加查询:
with:
queries: +.github/codeql/custom.ql,org/repo/query.ql@v1
CodeQL Packs 是基于 YAML 的查询套件,用于创建、共享、依赖和运行 CodeQL 查询。它们可以通过 packs
参数进行设置:
with:
packs: +.github/codeql/pack1.yml,org/repo/pack2.yml@v1
CodeQL Packs 在本文写作时仍处于测试阶段。请参阅 [CodeQL Packs 文档](https://codeql.github.com/docs/codeql-cli/about-codeqlpacks/) 获取更多信息。 |
您还可以使用配置文件,例如 ./.github/codeql/codeqlconfig.yml
:
- uses: github/codeql-action/init@v1
with:
config-file: ./.github/codeql/codeql-config.yml
如果前述文件位于另一个私有仓库中,您可以添加一个访问令牌来加载查询、Packs 或配置文件:
external-repository-token: ${{ secrets.ACCESS_TOKEN }}
在配置文件中,通常会禁用默认查询并指定自己的查询。您还可以排除特定的路径。以下是一个示例 codeql-config.yml
:
name: "Custom CodeQL Configuration"
disable-default-queries: true
queries:
- uses: ./.github/codeql/custom-javascript.qls
paths-ignore:
- '**/node_modules'
- '**/test'
您的自定义查询套件(custom-javascript.qls
)可以导入来自 CodeQL Packs(codeql-javascript
)的其他查询套件(如 `javascript-security-extended.qls)并排除特定的规则:
- description: "Custom JavaScript Suite"
- import: codeql-suites/javascript-security-extended.qls
from: codeql-javascript
- exclude:
id:
- js/missing-rate-limiting
您还可以添加单个查询(- query : <path to query>
)、多个查询(- queries: <path to folder>
)或 Packs(- qlpack: <name of pack>
)。
CodeQL 非常强大,您有很多选项可以精细调整配置。有关更多详细信息,请参阅 [配置代码扫描](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-anderrors/configuring-code-scanning)。