一、先搞懂两个“安全测试工具”到底管啥
很多开发者在做代码检查、上线前安全验证的时候,都会碰到两个工具,一个叫SAST,一个叫DAST。不少人会问:这俩是不是重复的?用一个行不行?答案是不行,因为它们管的范围完全不一样,就像家里的“装修验收”和“入住后巡检”,各有各的作用,缺了哪个都可能留隐患。
1.1 SAST:代码写完后的“内部体检”
SAST说白了就是“静态代码分析”,它是在代码还没跑起来的时候,就对着代码本身做检查。就像你写完一篇作文,先自己读一遍,找错别字、病句、逻辑漏洞,不用把作文读给别人听、不用展示给别人看,只看内容本身就行。
举个例子,你写了一段用户登录的代码,里面直接把用户输入的账号密码拼到数据库查询语句里,这就是典型的SQL注入漏洞。SAST就能在代码编译、提交到代码库的时候,直接扫出来这种问题,告诉你这里有风险,得改。
1.2 DAST:程序跑起来后的“外部巡检”
DAST是“动态应用安全测试”,它是等代码编译成程序、跑起来之后,对着正在运行的程序做检查。就像你装修完房子,搬进去住之前,找个验房师从外面敲门、从窗户往里看、甚至假装陌生人敲门,看能不能随便闯进来,有没有漏雨、有没有插座漏电,这些都是在房子“正常运转”的时候才能测出来的。
还是拿登录功能举例,DAST会模拟一个真实的黑客,往登录框里输一些特殊的字符(比如单引号、特殊符号),看程序会不会报错、会不会直接返回数据库里的敏感数据,或者会不会绕过登录直接进后台。这些问题是SAST扫不出来的,因为只有程序跑起来,才会出现和数据库交互、和其他模块联动的问题。
二、为啥必须互补?单靠一个会漏啥?
很多人觉得SAST能扫代码里的所有问题,或者DAST能覆盖所有安全隐患,其实不然,两者的盲区刚好能互相填补,单靠一个工具会漏掉很多关键的风险。
2.1 SAST的盲区:只看代码,不看“实际运行”
SAST的核心是分析代码本身,它有几个天生的盲区: 第一,它扫不到第三方依赖的问题。比如你用了一个开源的登录组件,这个组件本身有漏洞,但你的代码里只是调用了这个组件,没有修改组件的核心代码,SAST可能就扫不出来。 第二,它扫不到代码运行时的配置问题。比如你把数据库的账号密码写在了配置文件里,配置文件里的密码是明文的,但你的代码只是读取这个配置,没有硬编码密码,SAST可能只会提醒你“不要硬编码密码”,但不会告诉你配置文件里的明文密码有风险。 第三,它扫不到多模块联动的问题。比如你的代码里有A模块和B模块,A模块的代码本身没问题,B模块的代码本身也没问题,但A模块传参数给B模块的时候,没有做参数校验,导致B模块出现了XSS漏洞,SAST可能就扫不出来,因为它是逐个模块分析的,不是按运行流程分析的。
举个SAST漏扫的例子:假设你用了一个开源的JSON解析库,这个库有一个已知的漏洞,会导致远程代码执行,但你的代码只是调用了这个库的解析方法,没有修改库的代码,SAST就扫不出来,因为它只看你自己写的代码,不看你引入的第三方库的代码。
2.2 DAST的盲区:只看表面,不看“内部逻辑”
DAST的核心是模拟攻击正在运行的程序,它也有几个天生的盲区: 第一,它扫不到未公开的接口。比如你的程序里有一个内部用的接口,只有内网能访问,没有对外暴露,DAST如果只扫对外的接口,就扫不到这个内部接口的问题。 第二,它扫不到代码里的硬编码漏洞。比如你的代码里硬编码了一个管理员的密码,这个密码是写死在代码里的,但程序运行的时候,这个密码不会出现在对外的接口里,DAST就扫不出来,因为它只能测对外暴露的功能。 第三,它扫不到逻辑上的漏洞。比如你的程序里有一个“找回密码”的功能,逻辑是“输入手机号,发送验证码,输入验证码后可以修改密码”,但代码里有一个漏洞:如果用户输入的手机号是别人的,也能收到验证码,DAST可能扫不出来,因为它只会测输入手机号后有没有收到验证码,不会测这个手机号是不是当前用户的。
举个DAST漏扫的例子:假设你的代码里硬编码了一个管理员的密码“admin123”,这个密码只有内部人员知道,对外的登录接口里不会返回这个密码,DAST就扫不出来,因为它只能测对外暴露的功能,测不到代码里的硬编码内容。
三、完整的安全测试覆盖体系怎么搭?
要构建完整的安全测试覆盖体系,就得把SAST和DAST结合起来,再加上一些辅助的工具和流程,形成一个“从代码到运行”的全流程覆盖。
3.1 第一步:代码提交前的SAST检查
在开发者提交代码到代码库之前,就要做SAST检查,把问题消灭在萌芽状态。这里要注意,SAST工具要配置得足够灵活,不能太严也不能太松。太严的话,会导致很多误报,开发者疲于应付;太松的话,又会漏掉很多问题。
举个具体的SAST配置示例(技术栈:Java,用SonarQube作为SAST工具): 首先,在SonarQube里配置规则,开启SQL注入、XSS、硬编码密码等规则,关闭一些和业务无关的规则。然后,在开发者的本地开发环境里,配置SonarLint插件,开发者写完代码后,先在本地扫一遍,确认没有问题再提交。
// 示例:有SQL注入漏洞的代码
public User getUser(String username) {
String sql = "SELECT * FROM user WHERE username = '" + username + "'"; // 直接拼接用户输入,有SQL注入风险
return jdbcTemplate.queryForObject(sql, User.class);
}
这段代码里,开发者直接把用户输入的username拼到SQL语句里,SonarLint会直接扫出来,提醒开发者“不要直接拼接用户输入到SQL语句里”,开发者就可以改成用预编译的方式:
// 示例:修复后的代码
public User getUser(String username) {
String sql = "SELECT * FROM user WHERE username = ?"; // 用占位符,避免SQL注入
return jdbcTemplate.queryForObject(sql, new Object[]{username}, User.class);
}
这样,在代码提交前,就把SQL注入的问题解决了。
3.2 第二步:代码编译后的SAST二次检查
代码提交到代码库后,在编译成程序之前,还要做一次SAST二次检查,避免开发者本地配置的问题,或者提交的时候漏扫。这一步可以把SAST工具集成到CI/CD流程里,代码提交后自动触发检查,如果检查不通过,就不能继续编译。
举个CI/CD集成SAST的示例(技术栈:Java,用Jenkins作为CI工具,SonarQube作为SAST工具): 在Jenkins里配置一个任务,当代码提交到Git仓库后,自动触发这个任务,任务里先拉取代码,然后运行SonarScanner,把代码提交到SonarQube做分析,如果分析结果里有高危漏洞,就自动终止编译流程,通知开发者修复。
# 示例:Jenkins里运行SonarScanner的命令
sonar-scanner \
-Dsonar.projectKey=my-project \
-Dsonar.sources=src/main/java \
-Dsonar.host.url=http://sonarqube-server:9000 \
-Dsonar.login=admin \
-Dsonar.password=admin
这段命令会把src/main/java目录下的代码提交到SonarQube做分析,如果分析结果里有高危漏洞,就会返回非零的退出码,Jenkins就会终止流程。
3.3 第三步:程序上线前的DAST检查
程序编译成可运行的包后,要部署到测试环境,然后做DAST检查,测程序在运行状态下的安全问题。这一步要注意,DAST工具要模拟真实的攻击场景,不能只做简单的扫描。
举个DAST检查的示例(技术栈:Java,用OWASP ZAP作为DAST工具): 首先,把程序部署到测试环境,然后运行OWASP ZAP,配置目标地址为测试环境的地址,开启SQL注入、XSS、跨站请求伪造等扫描规则,扫描完成后,查看扫描结果。
# 示例:OWASP ZAP扫描命令
zap-baseline.py -t http://test-server:8080 -r report.html
这段命令会扫描http://test-server:8080这个地址,生成一个扫描报告report.html,报告里会列出所有的漏洞,比如SQL注入、XSS等。
如果扫描结果里有高危漏洞,比如远程代码执行,就不能上线,要通知开发者修复,修复后再重新做DAST检查。
3.4 第四步:上线后的DAST巡检
程序上线后,还要定期做DAST巡检,因为程序在运行过程中,可能会有新的漏洞出现,比如第三方依赖的漏洞被披露,或者业务逻辑的调整导致新的安全问题。
举个上线后DAST巡检的示例(技术栈:Java,用OWASP ZAP作为DAST工具): 可以配置一个定时任务,每周运行一次OWASP ZAP,扫描生产环境的地址,生成扫描报告,发送给安全团队和开发团队。如果扫描结果里有高危漏洞,就及时修复。
四、应用场景、优缺点和注意事项
4.1 应用场景
SAST和DAST的组合适用于所有需要做安全测试的场景,尤其是以下几个场景: 第一,互联网产品的上线前安全检查,比如电商网站、社交APP、金融APP等,这些产品涉及大量的用户数据和交易,安全要求高。 第二,企业内部系统的安全检查,比如OA系统、CRM系统、ERP系统等,这些系统涉及企业的内部数据和业务流程,安全要求也很高。 第三,开源项目的安全检查,比如开源的组件、框架、工具等,这些项目被大量的开发者使用,一旦有漏洞,影响范围很广。 第四,合规性检查,比如等保2.0、PCI DSS等合规要求,都要求做静态代码分析和动态应用安全测试。
4.2 技术优缺点
SAST的优点: 第一,能在代码开发阶段就发现问题,修复成本低,比如在代码提交前修复一个漏洞,成本只有上线后修复的1/10。 第二,能覆盖所有的代码,包括未公开的接口、内部的逻辑等。 第三,能和CI/CD流程集成,实现自动化检查,提高效率。
SAST的缺点: 第一,误报率高,比如有些代码本身没有问题,但SAST工具会误判为有漏洞,开发者需要花时间去验证。 第二,扫不到第三方依赖的问题、配置的问题、多模块联动的问题等。 第三,需要开发者有一定的安全知识,才能理解SAST的扫描结果,修复漏洞。
DAST的优点: 第一,能模拟真实的攻击场景,发现程序在运行状态下的安全问题,比如SQL注入、XSS等。 第二,误报率低,因为它是基于程序的实际运行结果来判断的,不是基于代码本身。 第三,不需要了解程序的代码,只要知道程序的地址和端口,就能做扫描,适合第三方的安全检查。
DAST的缺点: 第一,只能测对外暴露的接口,扫不到内部的接口、未公开的逻辑等。 第二,修复成本高,因为它是在程序上线前或者上线后发现问题,修复需要重新编译、部署,成本高。 第三,可能会影响程序的正常运行,比如扫描的时候会产生大量的请求,导致程序的性能下降,甚至崩溃。
4.3 注意事项
第一,要选择适合自己技术栈的工具,比如Java项目适合用SonarQube做SAST,用OWASP ZAP做DAST;Python项目适合用Bandit做SAST,用Burp Suite做DAST。 第二,要配置合适的规则,不能太严也不能太松,比如SAST工具要关闭一些和业务无关的规则,DAST工具要开启一些和业务相关的规则。 第三,要结合人工检查,因为SAST和DAST都可能会漏扫,比如逻辑上的漏洞,需要人工检查才能发现。 第四,要定期更新工具的规则和漏洞库,因为新的漏洞会不断出现,只有更新了规则和漏洞库,才能扫到新的漏洞。 第五,要保护好扫描结果,因为扫描结果里会有程序的漏洞信息,一旦泄露,会被黑客利用,造成安全事故。
五、文章总结
SAST和DAST是两种不同的安全测试工具,它们的作用范围、测试阶段、优缺点都不一样,单靠一个工具会漏掉很多安全隐患。只有把SAST和DAST结合起来,再加上一些辅助的工具和流程,形成一个“从代码到运行”的全流程覆盖,才能构建完整的安全测试覆盖体系,保障程序的安全。
SAST管的是代码内部的问题,在代码开发阶段就发现问题,修复成本低;DAST管的是程序运行后的问题,在程序上线前和上线后发现问题,能模拟真实的攻击场景。两者互补,才能覆盖所有的安全测试场景,避免出现安全事故。
在实际应用中,要根据自己的业务需求和技术栈,选择合适的工具,配置合适的规则,结合人工检查,定期更新工具的规则和漏洞库,才能真正发挥SAST和DAST的作用,保障程序的安全。
Comments