Introduction

在这个房间里,我们将研究静态应用程序安全测试 (SAST),它是在应用程序开发生命周期中用于检测漏洞的众多工具之一。我们将研究它的优点和缺点,以及它如何补充其他方法,以确保应用程序在开发早期的安全。

学习目标

  • 了解如何应用 SAST 来查找实际应用程序中的弱点。
  • 了解使用 SAST 的利弊。
  • 熟悉如何在开发生命周期中实施 SAST。

房间先决条件

在继续这个房间之前,熟悉 Web 漏洞非常重要,如 OWASP Top 10OWASP API Top 10 房间中所示。掌握一些软件开发生命周期各个阶段的基本知识也是可取的,因此,如有需要,请随时查看 SDLC 室

Code Review

开发安全应用程序的方法之一是通过称为代码审查的过程频繁测试代码是否存在安全漏洞。代码审查包括查看应用程序的源代码,使用白盒方法搜索可能的漏洞。通过访问代码,审查者可以保证更大程度地覆盖应用程序的功能,并减少查找错误所需的时间。

如果不加以处理,在开发过程早期引入的漏洞将传播到项目结束时,而解决漏洞将更加费力且成本更高。代码审查将尝试尽早发现此类漏洞,因为这些漏洞更容易修复。

手动与自动代码审查

为了获得最佳结果,代码审查通常通过手动分析和自动工具的组合来完成。这两种方法都有优点,在决定开发生命周期的每个阶段哪种方法最好时需要考虑这些优点。

一方面,手动代码审查的优势在于人工评估代码,这可以进行彻底的分析并获得更精确的结果。但是,由于应用程序通常有成千上万行代码,因此这项任务很快就会让审查者感到不堪重负,导致他们因疲劳而错过一些漏洞。

另一方面,自动化工具擅长几乎立即发现常见漏洞,为审查者节省了大量时间。无论代码库的大小如何,自动化工具也能始终如一地执行,因此只要它们有预定义的规则来匹配漏洞,它们就不会像人类那样错过漏洞。如果工具没有为特定类型的漏洞配置规则,它们很可能会错过这些规则。

另一个需要比较的重要方面是成本。人工审查的成本通常更高,因为审查者必须花费大量时间通过代码追踪漏洞。自动化工具几乎可以立即执行分析。

出于这些原因,您通常希望在开发生命周期的早期运行自动化测试,以较低的成本处理所有容易实现的问题,并定期或在实现重要项目目标时进行人工审查,以处理自动化工具可能无法检测到的复杂漏洞。

Manual Code Review

在开始介绍 SAST 工具之前,我们将了解手动核心审查通常是如何执行的,因为这将使我们更容易理解 SAST 工具如何查看代码。

在此任务中,我们的目标是在简单应用程序的源代码中找到 SQL 注入漏洞。该应用程序还有其他漏洞,但我们将专注于一种类型的漏洞,以了解传统代码审查是如何进行的,以及这些步骤如何映射到 SAST 工具使用的分析技术。

在继续之前,请按下此任务附带的绿色 启动机器 按钮,确保您的虚拟机正在运行。虚拟机正在拆分视图中启动。如果看不到机器,请按下此房间右上角的蓝色 显示拆分视图 按钮。

对于此任务,我们将使用的代码可以在 /home/ubuntu/Desktop/simple-webapp/ 中找到

搜索不安全函数

审查代码的第一步是识别正在使用的潜在不安全函数。由于我们正在寻找 SQL 注入,因此我们应该关注可用于向数据库发送原始查询的任何函数。由于我们的代码使用 PHP 和 MySQL,因此以下是一些通常用于发送 MySQL 查询的函数:

数据库引擎 函数
MySQL mysqli_query() mysql_query() mysqli_prepare() query() prepare()

手动搜索此类函数实例的一种直接方法是使用 grep 递归检查我们项目的所有文件。例如,要搜索 mysqli_query() 的实例,请转到项目的基目录并运行以下命令:

Linux

user@machine$ cd /home/ubuntu/Desktop/simple-webapp/html/

user@machine$ grep -r -n 'mysqli_query('
db.php:18: $result = mysqli_query($conn, $query);

-r 选项告诉 grep 递归搜索当前目录下的所有文件,而 -n 选项表示我们希望 grep 告诉我们找到模式的行号。在上面的终端输出中,我们可以看到在行 18 上发现了一个 mysqli_query() 实例。

我们已经确定了可能导致 SQL 注入的一行。但是,仅通过查看该行,我们无法确定是否存在漏洞。

了解上下文

让我们打开 db.php 来分析 mysqli_query() 函数周围的上下文,看看我们是否能更好地了解它的使用方式:

function db_query($conn, $query){
$result = mysqli_query($conn, $query);
return $result;
}

这里我们可以看到 mysqli_query() 被包装到 db_query() 函数中,并且 $query 参数直接传递而无需修改。函数嵌套到其他函数中是很常见的,因此仅仅分析函数的本地上下文有时不足以确定是否存在漏洞。我们现在需要在整个代码中跟踪 db_query() 函数的使用情况,以识别潜在的漏洞。

跟踪用户对潜在漏洞函数的输入

我们可以再次使用 grep 来搜索 db_query() 的使用情况:

Linux

user@machine$ grep -rn 'db_query('
hidden-panel.php:7:$result = db_query($conn, $sql);
hidden-panel.php:20:$result2 = db_query($conn, $sql2);
hidden-panel.php:23:$result3 = db_query($conn, $sql3);
db.php:17:function db_query($conn, $query){

这里我们在“hidden-panel.php”上找到了三种“db_query()”的用法。再次,我们可以分析每个调用的上下文,从第 7 行的调用开始:

$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE id=".$_GET['guest_id'];
$result = db_query($conn, $sql);

恭喜您找到 SQL 注入!通过 GET 方法传入 guest_id 参数的任何内容都将连接到原始 SQL 查询,而不会进行任何输入清理,从而使攻击者能够更改查询。

如果我们对 db_query() 的其他两种用法执行类似的过程,我们将得到以下代码:

$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";
$result2 = db_query($conn, $sql2);

$sql3 = "SELECT id, name FROM asciiart WHERE id=".preg_replace("/[^0-9]/", "", $_GET['art_id'], 1);
$result3 = db_query($conn, $sql3);

同样,我们将一些 GET 参数连接到 SQL 查询中。这会给我们留下这样一种印象:我们又发现了两个漏洞。不过,在这两种情况下,GET 参数都通过 preg_replace() 进行清理,使用不同的正则表达式将任何不允许的字符替换为空字符串。要确定这些代码行是否存在漏洞,我们需要评估现有的过滤器是否允许 SQL 注入通过。

$sql2 的查询通过一个过滤器,该过滤器只允许字母数字或双引号字符。虽然双引号似乎是 SQLi 的可能载体,但在这种情况下它们并不构成威胁,因为 SQL 语句将通过 $_GET['log_id'] 传递的字符串括在单引号 (‘) 之间。由于攻击者无法逃脱 SQL 语句中的字符串,因此我们可以确定此行不存在漏洞。

$sql3 上的查询更加严格,只允许通过 $_GET['art_id'] 传递数字字符。但是,请注意,preg_replace() 函数在调用时将第三个参数设置为“1”。如果我们参考 函数手册页,我们将了解到第三个参数表示要进行的最大替换次数。由于它设置为 1,因此只有第一个非数字字符将被替换为空字符串。任何其他字符都将通过而不被替换,从而启用 SQL 注入。此行确实存在漏洞。

足够的手动审查

我们已经使用手动代码审查来识别应用程序中的两个 SQL 注入点。虽然示例应用程序非常简单,但实际应用程序有更多的代码行。跟踪潜在易受攻击函数的每个实例将更加复杂。到目前为止,应该很明显,手动审查大型代码库很快就会成为一项繁琐的任务。现在,我们将使用 SAST 工具为我们执行相同的分析,并分析它们的性能。

奖励练习

使用手动代码审查来查找所提供项目中的其他类型的漏洞。以下问题将指导您完成此过程。

Automated Code Review

静态应用程序安全测试

静态应用程序安全测试 (SAST) 是指使用自动化工具进行代码分析。其目的不是取代手动代码审查,而是提供一种简单的方法来自动执行简单的代码检查,以便在开发过程中快速发现漏洞,而无需专业人员。

SAST 是对其他技术(如动态应用程序安全测试 (DAST) 或软件组合分析 (SCA))的补充,以在开发生命周期内提供应用程序安全的整体方法。与任何其他技术一样,SAST 也有其优点和缺点,我们需要注意:

优点:它不需要目标应用程序的运行实例。它提供了对应用程序功能的良好覆盖。与其他动态技术相比,它的运行速度更快。SAST 工具可以准确报告代码中的漏洞位置。易于集成到您的 CI/CD 管道中。 缺点:应用程序的源代码并不总是可用的(第三方应用程序)。容易出现误报。无法识别本质上是动态的漏洞。SAST 工具大多是特定于语言的。它们只能检查它们知道的语言。

SAST 内部原理

虽然每个 SAST 工具都不同,但大多数工具都会执行两个主要任务:

  1. 将代码转换为抽象模型:SAST 工具通常会提取源代码并生成抽象表示以供进一步分析。大多数 SAST 工具将使用抽象语法树 (AST) 表示代码,但某些工具可能具有其他等效的专有结构。这样可以更轻松地以独立于所用编程语言的方式进行代码分析。此步骤对于后续分析至关重要,因为任何未正确转换为 AST 的编程语言特性都可能无法有效地分析安全问题。

  2. 分析抽象模型的安全问题:将使用不同的分析技术来搜索代码模型中的潜在漏洞。

在这个房间中,我们不会详细介绍代码建模,因为您不必从用户的角度来处理它。相反,我们将重点介绍 SAST 工具常用的不同分析技术:

Semantic Analysis 语义分析:这种分析可以比作在进行手动代码审查时对潜在不安全函数的 grepping。它旨在发现与本地环境中使用潜在不安全代码有关的缺陷。例如,搜索对 mysqli_query() 的调用,其中 GET 或 POST 参数直接连接到查询字符串中:mysqli_query($db, "SELECT * from users where username=".$_GET['username'])
Dataflow Analysis 数据流分析:在某些情况下,可能会使用存在潜在危险的函数,但仅通过分析函数调用周围的本地上下文无法确定是否存在漏洞。例如,一个定义如下的函数:function db_query($conn, $query){ $result = mysqli_query($conn, $query); return $result; }通过查看函数的代码,很难判断是否存在任何漏洞。在这种情况下,仅分析对危险函数的调用的本地上下文是不够的。数据流分析将跟踪信息如何从用户可以操纵的输入流向潜在易受攻击的函数,就像我们手动分析提供的应用程序代码时所做的那样。在数据流分析术语中,数据输入将被称为,潜在易受攻击的函数将被称为接收器。如果数据未经清理就从源流向接收器,则存在漏洞。回到db_query()函数,数据流分析必须找到它的所有用法并追溯以查看是否有任何受污染的输入(源)被发送给它,最终成为mysqli_query()(接收器)执行的查询的一部分。Dataflow Graph
Control Flow Analysis 控制流分析:分析代码中的操作顺序,以查找竞争条件、未初始化变量的使用或资源泄漏。例如,查看以下 Java 代码:String cmd = System.getProperty(“cmd”); cmd = cmd.trim();如果未定义 cmd 属性,则对 System.getProperty() 的调用将返回 NULL。从 NULL 变量调用 trim 方法将在运行时引发异常。
Structural Analysis 结构分析:分析每种编程语言的特定代码结构。这包括在声明类时遵循最佳实践、评估可能永远不会执行的代码块(死代码)、正确使用 try/catch 块以及与使用不安全加密材料(弱密钥或 IV)相关的其他问题。以下是结构分析可以检测到的代码的简单示例:$options = array('private_key_bits' => 1024, 'private_key_type' => OPENSSL_KEYTYPE_RSA); $res = openssl_pkey_new($options);在这种情况下,我们有一个密钥大小为 1024 的 RSA 实现,按照今天的标准,这被认为是不够的。
Configuration Analysis 配置分析:搜索应用程序配置缺陷,而不是代码本身。例如,在 Internet 信息服务下运行的应用程序将有一个名为“web.config”的配置文件,PHP 会将其所有配置选项保存在一个名为“php.ini”的文件中,大多数应用程序都会使用一些配置文件。通过检查配置,该工具将识别可能的改进。以下是 PHP 中两个配置指令的示例,它们可能会对 SAST 工具发出警报,因为它们为 RFI、SSRF 或其他攻击媒介提供了便利:allow_url_include = On allow_url_fopen = On

值得注意的是,并非每个 SAST 工具都会实现我们讨论的所有分析技术,但最好将它们作为参考来比较现有的不同解决方案。

Rechecking our Application with SAST Tools

现在我们知道了 SAST 工具的工作原理,我们准备使用其中的一些工具。我们将使用一个简单的 PHP 应用程序作为此任务的目标。您可以在桌面上的 simple-webapp 文件夹中找到该应用程序的代码。

使用 Psalm 重新检查我们的应用程序

我们将首先使用 Psalm(PHP 静态分析 Linting Machine),这是一个用于分析 PHP 代码的简单工具。该工具已作为应用程序项目的一部分安装,因此您无需安装它,但如果需要,您可以在 Psalm 的在线文档 上找到安装说明。

打开终端并转到项目的目录。您将在项目的根目录中找到一个 psalm.xml 文件。这是 Psalm 的配置文件,它应该如下所示:

<?xml version="1.0"?>
<psalm
errorLevel="3"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
findUnusedBaselineEntry="true"
>
<projectFiles>
<directory name="html/" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

我们不会深入介绍配置选项,但在这里您可以看到 errorLevel 参数,它表示 Psalm 在报告问题时的严格程度。值越低,报告的问题越多。还有一个名为 projectFiles 的部分,其中指示要扫描的文件。对于此项目,只会扫描 html 目录,而供应商目录将被忽略(因为我们不想测试第三方依赖项)。

设置配置文件后,您可以在项目目录中使用以下命令运行 Psalm:

Linux

user@machine$ cd /home/ubuntu/Desktop/simple-webapp/
user@machine$ ./vendor/bin/psalm --no-cache

ERROR: TypeDoesNotContainType - html/hidden-panel.php:10:5 - Operand of type 0 is always falsy (see https://psalm.dev/056)
if ($result->num_rows = 0) {

默认情况下,Psalm 只会对代码运行一些结构分析,并向我们显示需要注意的编程错误。在前面的例子中,Psalm 报告 if 子句的条件始终为 false。在这种情况下,程序员错误地使用了赋值运算符(=)而不是比较运算符(==)。由于赋值运算符始终返回分配的值(在本例中为 0),因此条件将始终计算为 false(在 PHP 中,0 将自动转换为 false)。

默认测试将搜索变量类型、变量初始化和其他安全编码模式的问题。虽然这些问题不是漏洞,但 Psalm 会提出建议,以便您的代码遵循编码最佳实践,这是避免运行时错误的一个好开始。

Psalm 还提供了使用 --taint-analysis 标志对我们的代码运行数据流分析的可能性。此命令的输出将更加有趣,因为它将精确定位可能的安全问题。命令的输出将如下所示:

Linux

user@machine$ cd /home/ubuntu/Desktop/simple-webapp/
user@machine$ ./vendor/bin/psalm --no-cache --taint-analysis

ERROR: TaintedInclude - html/view.php:22:9 - Detected tainted code passed to include or similar (see https://psalm.dev/251)
$_GET
<no known location>

$_GET['img'] - html/view.php:22:28
include('./gallery-files/'.$_GET['img']);

concat - html/view.php:22:9
include('./gallery-files/'.$_GET['img']);

对于报告的每个错误,Psalm 都会向您显示从源(“$_GET”)到接收器函数(“include()”)的完整数据流跟踪。这里我们有一个典型的本地文件包含 (LFI) 漏洞,因为 GET 参数“img”直接连接到传递给“include()”函数的文件名中。

从安全角度来看,数据流分析通常会获得最有趣的发现。但是,其他结构性发现同样重要,即使它们不会直接转化为可利用的漏洞。结构性缺陷通常会导致难以跟踪的业务逻辑错误或应用中的一些不可预测的行为。

处理误报和误报

无论您使用哪种 SAST 工具,错误总是存在的。作为快速提醒,我们通常会关注以下两种错误类型:

  • 误报:该工具报告代码中不存在的漏洞。
  • 漏报:该工具不会报告代码中存在的漏洞。

这些错误可能是因为该工具无法正确评估目标代码而出现的,但如果我们作为用户没有正确使用该工具,这些错误也会出现。

举一个简单的例子,当我们之前手动检查代码时,我们发现了三个可能的 SQL 注入实例。尽管如此,我们还是丢弃了其中一个,因为字符过滤正在有效地应用于它,留下两个确认的 SQL 注入漏洞。如果我们检查 Psalm 的输出,我们会注意到只报告了一个 SQL 注入实例(第一个在 hidden-panel.php6-7 行中):

Linux

user@machine$ ./vendor/bin/psalm --no-cache --taint-analysis

ERROR: TaintedSql - html/db.php:18:32 - Detected tainted SQL (see https://psalm.dev/244)
$_GET
<no known location>

$_GET['guest_id'] - html/hidden-panel.php:6:65
$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE id=".$_GET['guest_id'];

concat - html/hidden-panel.php:6:8
$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE id=".$_GET['guest_id'];

作为一个实验,让我们注释掉 hidden-pannel.php 中的第 6 行和第 7 行,看看会发生什么:

// $sql = "SELECT id, firstname, lastname FROM MyGuests WHERE id=".$_GET['guest_id'];
// $result = db_query($conn, $sql);

理论上,我们不应该再有报告 TaintedSQL 实例的错误。但是,如果我们再次执行 Psalm,它现在将报告与其他两个 SQL 查询之一相对应的不同 TaintedSQL 错误:

Linux

user@machine$ ./vendor/bin/psalm --no-cache --taint-analysis

ERROR: TaintedSql - html/db.php:18:32 - Detected tainted SQL (see https://psalm.dev/244)
$_GET
<no known location>

$_GET['log_id'] - html/hidden-panel.php:19:87
$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";

call to preg_replace - html/hidden-panel.php:19:87
$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";

现在,您可能想知道为什么 Psalm 没有第一次检测到此漏洞。如果您检查两个错误的第一行,就可以找到原因。两者都显示 db.php 的第 18 行是问题的根源。对于 Psalm,这两个漏洞是相同的,并且与您在 db.php 的第 18 行调用 mysqli_query() 的方式有关。换句话说,Psalm 希望您在那里应用修复,因为它不明白代码将 db_query() 定义为任何数据库查询的包装器。

在继续之前,请确保取消注释 hidden-panel.php 中的第 6-7 行:

$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE id=".$_GET['guest_id'];
$result = db_query($conn, $sql);

为了更好地帮助 Psalm 了解情况,我们可以使用一些注释来提供更多上下文。我们可以指定 db_query() 应被视为接收器,因此如果任何受污染的输入通过 $query 参数到达该函数,我们就会收到错误。为此,在 db.php 中的 db_query() 函数定义顶部添加以下注释:

/**
* @psalm-taint-sink sql $query
* @psalm-taint-specialize
*/
function db_query($conn, $query){
$result = mysqli_query($conn, $query);
return $result;
}

@psalm-taint-sink 注释告诉 Psalm 检查 $query 参数中是否存在类型为 sql 的受污染输入。现在,每次受污染输入到达 db_query() 时,Psalm 都会发出 TaintedSQL 错误。

我们还需要添加 @psalm-taint-specialize 注释来告诉 Psalm 每次调用该函数都应被视为一个单独的问题,并且该函数的污染程度完全取决于它收到的输入。这样,我们将分别得到这两个问题。

重新运行 Psalm 后,我们现在应该可以按预期获得所有 TaintedSQL 错误实例:

Linux

user@machine$ ./vendor/bin/psalm --no-cache --taint-analysis

ERROR: TaintedSql - html/db.php:21:26 - Detected tainted SQL (see https://psalm.dev/244)
$_GET
<no known location>

$_GET['guest_id'] - html/hidden-panel.php:6:65
$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE id=".$_GET['guest_id'];

concat - html/hidden-panel.php:6:8
$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE id=".$_GET['guest_id'];

$sql - html/hidden-panel.php:6:1
$sql = "SELECT id, firstname, lastname FROM MyGuests WHERE id=".$_GET['guest_id'];

call to db_query - html/hidden-panel.php:7:27
$result = db_query($conn, $sql);

ERROR: TaintedSql - html/db.php:21:26 - Detected tainted SQL (see https://psalm.dev/244)
$_GET
<no known location>

$_GET['log_id'] - html/hidden-panel.php:19:87
$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";

call to preg_replace - html/hidden-panel.php:19:87
$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";

preg_replace#3 - html/hidden-panel.php:19:87
$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";

preg_replace - vendor/vimeo/psalm/stubs/CoreGenericFunctions.phpstub:1182:10
function preg_replace($pattern, $replacement, $subject, int $limit = -1, &$count = null) {}

concat - html/hidden-panel.php:19:9
$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";

concat - html/hidden-panel.php:19:9
$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";

$sql2 - html/hidden-panel.php:19:1
$sql2 = "SELECT id, logtext FROM logs WHERE id='".preg_replace('/[^a-z0-9A-Z"]/', "", $_GET['log_id']). "'";

call to db_query - html/hidden-panel.php:20:28
$result2 = db_query($conn, $sql2);

注意:您将收到重复前一个错误的第三个错误。这是因为 mysqli_query 仍被视为有效接收器(Psalm 有一个内置的默认接收器列表),因此您仍然应该将原始警报置于新警报之上。

最后,如果您将 Psalm 的结果与我们的手动审查进行比较,您应该注意到结果中存在一些差异:

Manual Review Psalm Review Verdict
$sql Vulnerable Vulnerable OK
$sql2 Not Vulnerable Vulnerable False Positive
$sql3 Vulnerable Not Vulnerable False Negative

使用 SAST 工具时,总会存在一定程度的误报和漏报。与任何其他工具一样,您必须手动检查报告以查找任何错误。SAST 是手动审核的绝佳补充,但绝不能将其视为其替代品。

SAST in the Development Cycle

SAST 是开发生命周期中最早出现的工具之一。它通常在编码阶段实施,因为使用它不需要有功能性应用程序。

SAST in the Development Cycle

根据具体情况,SAST 可以采用以下方式之一实现:

  • CI/CD 集成:每次发出拉取请求或合并时,SAST 工具都会检查代码是否存在漏洞。检查拉取请求可确保进行合并的代码至少经过了基本的安全检查。在某些情况下,与检查每个拉取请求相比,仅在合并时执行 SAST 扫描可能有助于防止开发管道速度变慢,因为它避免让所有开发人员等待完整的 SAST 扫描。
  • IDE 集成:SAST 工具可以集成到开发人员最喜欢的 IDE 中,而不必等待拉取请求或合并发生。这样,可以尽早修复代码,从而节省项目时间。

请注意,您可能希望 SAST 在开发人员的 IDE 和您的 CI/CD 管道上运行,这将是有效的。例如,IDE 工具可以运行基本的结构检查并执行安全编码指南,而 CI/CD 集成可能会运行更耗时的数据流/污点分析。请记住,您的具体设置将取决于每个项目的要求,团队应仔细评估。

如果您想了解如何将安全工具集成到 CI/CD 管道中,请查看 DAST 房间 ,获取将 DAST 工具集成到 Jenkins 管道的示例。使用 SAST 工具的过程非常相似,因此我们不会在此重复。

在 IDE 中集成 SAST

您的虚拟机已安装 VS Code 编辑器,并已配置为使用几个 SAST 工具:

  • Psalm:我们一直使用的工具支持 IDE 集成,方法是直接从 Visual Studio Marketplace 将 Psalm 插件安装到 VS Code 中。此插件将实时检查您输入的任何内容,并直接在您的代码中显示与控制台版本相同的警报。污点分析将不可用,因此您只能看到在基本 Psalm 分析中报告的结构问题。
  • Semgrep: 另一个可以直接从 Visual Studio Marketplace 安装到 VS Code 中的 SAST 工具。与 Psalm 一样,它会直接在您的代码中显示内联警报。Semgrep 甚至允许我们在需要时构建自定义规则。您可以在项目目录内的 semgrep-rules 目录中检查为此项目加载的规则。有关如何构建 Semgrep 规则的细节将留待以后讨论,所以现在不用担心。您可以在目录中检查规则,但这不是本讨论所需的。

这两种工具的工作方式类似,直接在代码中内联显示检测到的任何问题。唯一值得注意的区别是,Semgrep 会在您启动 VS Code 时运行,并向您显示它在所有文件上检测到的问题,而 Psalm 只会向您显示与您当前正在编辑的文件相关的问题。插件会将以下信息添加到您的 IDE 屏幕:

SAST in VS Code

对于检测到问题的每一行,您可以通过将鼠标指针悬停在其上来获取更多信息。以下是将鼠标指针悬停在“login.php”第 46 行上时会得到的信息:

Inline Problems in VS Code

您还可以通过单击 VS Code 屏幕底部的错误指示器来检查完整的问题列表:

Problems Panel

使用 IDE 检查问题可以让您作为开发人员编写出更清晰的代码,从而大大提高最终应用程序的整体安全性。

使用 IDE 中的两种工具,在任务结束时回答问题。

Conclusion

SAST 是我们可以用来在应用程序开发过程中提高其安全性的众多技术之一。我们已经展示了如何使用 Psalm(众多可用的 SAST 工具之一),以及与手动审核相比,它为我们节省了多少时间。与任何其他自动化工具一样,手动验证结果至关重要,因为可能会报告误报。

请务必查看 DAST 室,了解动态分析如何补充 SAST 以加强您的安全开发流程。