漏洞,Bug,Bad Smell 检查

2023-07-17, 星期一, 17:42

培训Cyber SecurityDevCI/CD

这是最容易解决的……大概。

Package Checker plugin

Jetbrains 系 IDE 提供了基础的开箱即用的依赖检查工具 Package Checker,该工具会扫描项目依赖并与 Checkmarx SCA Database 和 National Vulnerability Database 中的数据做对比,有漏洞的依赖项会被高亮标记。

鼠标点击灯泡按钮则会显示漏洞详情和建议解决方案(一般是升级到修复了该问题的版本)。

需要在 Plugins 菜单中确认 Package Checker 处于启用状态,并且配置中的 Inspections > Security 项目已被勾选。

OSV-Scanner

如果你的项目有多个 Module,或者你还有一大把工程文件需要处理,先不说一个一个打开依赖配置文件的效率有多高,CI pipeline 肯定是没法依赖 GUI 工具了。

OSV-Scanner 是 Google 基于其 OSV 数据库开发的漏洞扫描器,可以在 google/osv-scanner - Release 下载二进制文件,也可以通过 ghcr.io/google/osv-scanner:latest 在容器中运行。目前支持扫描 Java、JavaScript、Go、Python、Rust 等语言编写的项目,暂不支持 C/C++ 项目和非 Debian 基底的容器镜像。

简单来说,OSV-Scanner 会扫描指定目录中的 lockfiles、POM 等文件,通过 OSV.dev API 查询引用的依赖是否有漏洞记录。

osv-scanner --format table --config=osv-scanner.toml --experimental-call-analysis --recursive <path-to-the-project>
  • --format table 指定输出格式,其他格式有 markdown, json
  • --experimental-call-analysis 可以忽略未被实际使用的依赖
  • --config=osv-scanner.toml 目前只有忽略指定漏洞的功能

通过输出表格中的 OSV URL 可以查看漏洞详情,也可以用这个 OSV ID 在 open source insights 搜索,了解问题的严重程度和处理建议。

有些问题的严重程度不高,或者在生产环境中无法被利用,就可以在 osv-scanner.toml 中配置忽略,语法如下:

[[IgnoredVulns]]
id = "GO-2022-0968"
ignoreUntil = 2022-11-09
reason = "No ssh servers are connected to or hosted in Go lang"

[[IgnoredVulns]]
id = "GO-2022-1059"

OSV-Scanner 还可用于分析 Debian 基底的容器镜像。要检查容器中安装的软件包是否存在漏洞,使用 osv-scanner --docker <iamge-tag>

在 CI Pipeline 中集成漏洞扫描

OSV-Scanner 没有扫描到问题时,程序的 exit code 为 0,如果检查到问题则输出 1,编写 CI 脚本的时候可以利用这一点。例如在 Jenkins Pipeline 中设置:

stage("Vulnerability scan") {
    steps {
        sh '/usr/bin/osv-scanner --experimental-call-analysis --config=osv-scanner.toml --recursive .'
    }
}

如果 OSV-Scanner 检测到引用了存在漏洞的依赖,就会终止构建。

不过需要注意的是该服务受网络波动影响较大(尤其是在目前我们部署 Jenkins 的政务网环境),访问 api.osv.dev 可能会超时导致构建失败,暂时不建议这么做。

检查本地代码的问题

使用 SonarLint 规范代码

SonarLint 是 Sonatype(就是那个发布了 Maven 私服管理工具 Nexus 的公司,以及往 Maven 中央仓库发布软件包时托管服务的主要提供者)发布的可本地部署运行的代码检查工具。

在 IntelliJ IDEA 或 Visual Studio Code 的插件市场中搜索并安装 SonarLint。以 IntelliJ IDEA 为例,工具栏会出现 SonarLint 按钮,打开后选择 Report 栏,左侧会出现如图的小图标。

由于我们是第一次使用,选择这个文件夹样式的图标扫描整个项目,之后可通过上面那个 Git 分支模样的按钮进行增量扫描。

一段时间后工具将会输出报告,比如我现在演示的项目就在 126 个文件中找到了 479 个问题,在 21 个文件中找到 73 处潜在的 Security Hotspot。点击其中一处详情可以跳转到问题代码,同时插件窗口会展示详情。

SonarLint 把问题分为如下几类:

  • Bug
  • Vulnerability:存在漏洞
  • Code Smell:不推荐的写法
  • Security Hotspot:需要人工复审的代码

对于前三种问题,SonarLint 会指出什么地方出了问题,应该如何解决,并给出代码示例。

对于 Security Hotspot,SonarLint 会提供三个 tab 页:What's the risk? 说明为什么 SonarLint 认为这是一个问题,Access the risk 则帮助开发人员判断这是不是一个需要解决的问题,而 How can I fix it? 则如同其命名那样提供了解决方案。

这里的代码在捕获到异常时选择了 ex.printStackTrace(),这是一个严重程度较低的问题。SonarLint 建议使用日志收集器的对应方法代替 ex.printStackTrace(),同时开启 Spring Framework 的 EnableWebSecurity 注解。

在另一处示例中,SonarLint 提出 algorithm = MessageDigest.getInstance("MD5") 使用了容易碰撞的 Hash 方法,建议替换为 SHA-512,这就需要开发者在安全和性能间权衡后选择是否遵循了。

当然,目前 SonarLint 也存在一些问题。例如在下列代码中没有意识到初始向量 iv 其实是由generateIV 方法随机生成的,给出了 Use a dynamically-generated, random IV 的警告。

byte[] iv = generateIV();
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));

这个问题可以配合 SonarCube 管理端标记为 false positive,不过本地单独使用的话确实没有什么办法。