SonarQube实现自动化代码扫描

· 2020-04-27 20:35 · 6 阅读

原创 bloodzer0 2020-04-27 20:35

记录使用SonarQube实现DevSecOps中代码扫描的经历

SonarQube官网地址:https://www.sonarqube.org/ 
SonarQube安装



Centos7安装SonarQube
# 解决依赖,SonarQube7.9必须是java11才可以运行yum install java-11-openjdk.x86_64 java-11-openjdk-devel.x86_64 -y
# 安装PG数据库,新版本已经不支持MySQL了wget https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpmrpm -ivh pgdg-redhat-repo-latest.noarch.rpmyum install postgresql10.x86_64 postgresql10-server.x86_64 postgresql10-libs.x86_64 postgresql10-libs.x86_64 postgresql-jdbc.noarch -y
# 初始化PG/usr/pgsql-10/bin/postgresql-10-setup initdb
# 允许本机连接vim /var/lib/pgsql/10/data/pg_hba.conf
# 启动PGsystemctl enable postgresql-10.servicesystemctl start postgresql-10.service
# 配置PGsu - postgrespsql
# 将template1取消设置为模板UPDATE pg_database SET datistemplate = FALSE WHERE datname = 'template1';
# 删除template1DROP DATABASE template1;
# 重新创建template1,并设置默认编码CREATE DATABASE template1 WITH TEMPLATE = template0 ENCODING = 'UTF8';
# 将template1重新设置为模板UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template1';
# 创建用户create user sonarqube with password 'sonarqube';alter role sonarqube login;
# 创建数据库create database sonarqube encoding='UTF8' owner sonarqube;
# 创建用户useradd sonarqubepasswd sonarqubesu - sonarqube
# 安装SonarQubewget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-8.0.zipunzip sonarqube-8.0.zip && cd sonarqube-8.0
# 修改配置文件vim /home/sonarqube/sonarqube-8.0/conf/sonar.properties
sonar.jdbc.username=sonarqubesonar.jdbc.password=sonarqubesonar.jdbc.url=jdbc:postgresql://127.0.0.1/sonarqube
# 配置Java版本# 由于8.0需要Java11,但是安装PG10的时候系统会默认安装Java1.8,所以需要配置Java地址vim /home/sonarqube/sonarqube-8.0/conf/wrapper.conf
# 我是yum安装的java11,如果大家是配置或者编译的需要使用对应的java地址wrapper.java.command=/usr/lib/jvm/java-11-openjdk-11.0.6.10-1.el7_7.x86_64/bin/java
# 启动服务./bin/linux-x86-64/sonar.sh start访问:http://10.10.10.12:9000,账号密码:admin/admin。
ES启动报错
  • 报错信息如下:

[1]max file descriptors [4096] for elasticsearch process is too lowincrease to at least [65535][2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
  • 解决方案如下:

vim /etc/sysctl.conf
# 添加如下内容vm.max_map_count=655360
# 立即生效sysctl -p
vim /etc/security/limits.conf
# 添加如下内容*       soft    nofile  65536*       hard    nofile  65536
# limits不需要重启,但是需要重新登录
注意:内存至少应该大于3G,否则容易出现ES不报错,但是自己停止运行。
 
Centos7安装SonarScanner
# 下载SonarScannerwget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.2.0.1873-linux.zip
unzip sonar-scanner-cli-4.2.0.1873-linux.zip -d /home/sonarqube/
SonarScanner执行扫描
  1. SonarQube管理⻚面创建一个Token68bec86cee8ec9a9ad8621592670c9939e6ba71e

  2. 执行扫描

/home/sonarqube/sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -Dsonar.host.url=http://127.0.0.1:9000 -Dsonar.login=68bec86cee8ec9a9ad8621592670c9939e6ba71e -Dsonar.projectKey=java-sec-code_scanner -Dsonar.java.binaries=/home/sonarqube/projects/java-sec-code
此时的扫描结果如下:

 
SonarQube集成FindBugs
Sonar-FindBugs GitHub地址:https://github.com/spotbugs/sonar-findbugs/releases/
集成方式有两种:
  • 应用市场中下载安装

  • GitHub下载jar文件放置到SonarQube的插件目录中

如果SonarQube集成了FindBugs,调用SonarScanner进行扫描时,必须对Java项目进行打包后再执行扫描。
使用FindBugs配置扫描
下图是SonarQube质量配置⻚面:

mvn clean package -DskipTests
/home/sonarqube/sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -Dsonar.host.url=http://127.0.0.1:9000 -Dsonar.login=68bec86cee8ec9a9ad8621592670c9939e6ba71e -Dsonar.projectKey=java-sec-code_findbugs -Dsonar.java.binaries=/home/sonarqube/projects/java-sec-code
此时的扫描结果如下:

使用FindBugs Security Audit配置扫描
通过在进行安全代码审计时,推荐使用的规则下图:

此时的扫描结果如下:

不加载FindBugs插件
如果你安装了FindBugs插件,又不想执行编译,那么在质量配置中将FindBugs设为默认的规则全部取即可。

 
SonarQube集成DependencyCheck
DependencyCheck-SonarQube GitHub地址:https://github.com/dependency-check/dependency-check-sonar-plugin
DependencyCheck GitHub地址:https://github.com/jeremylong/DependencyCheck/
SonarQube安装DependencyCheck插件与FindBugs一致。在使用SonarScanner执行扫描的时候,可以不编译项目,但是需要希望对jar包进行扫描则必须进行编译。
DependencyCheck使用命令
# 不更新漏洞库执行扫描/home/sonarqube/dependency-check/bin/dependency-check.sh -n
# 只更新漏洞库库,不执行扫描/home/sonarqube/dependency-check/bin/dependency-check.sh --updateonly
# 使用代理更新漏洞库/home/sonarqube/dependency-check/bin/dependency-check.sh --updateonly --proxyserver host --proxyport port
# 编译项目mvn clean package -DskipTests
# 生成xml报告/home/sonarqube/dependency-check/bin/dependency-check.sh -s /home/sonarqube/projects/java-sec-code/target/java-sec-code-1.0.0.jar -f XML -o /home/sonarqube/reports/java-sec-code-report.xml
# 在5.3.2 版本中,小B遇到了一个坑就是第一项执行DependencyCheck时,当执行到 Central Analyzer 分析时非常缓慢(由于网络原因导致的),可以使用--disableCentral禁用,但是官方文档中不推荐禁用,所以只能在执行是使用代理或调整网络来解决
# 生成html报告/home/sonarqube/dependency-check/bin/dependency-check.sh -s /home/sonarqube/projects/java-sec-code/target/java-sec-code-1.0.0.jar -f HTML -o /home/sonarqube/reports/java-sec-code-report.html
# 将报告导入SonarQube并执行SonarScanner扫描,这里我们使用的是FindBugs Security Audit审计规则/home/sonarqube/sonar-scanner-4.2.0.1873-linux/bin/sonar-scanner -Dsonar.host.url=http://127.0.0.1:9000 -Dsonar.login=68bec86cee8ec9a9ad8621592670c9939e6ba71e -Dsonar.projectKey=java-sec-code_dependency -Dsonar.java.binaries=/home/sonarqube/projects/java-sec-code -Dsonar.dependencyCheck.reportPath=/home/sonarqube/reports/java-sec-code-report.xml -Dsonar.dependencyCheck.htmlReportPath=/home/sonarqube/reports/java-sec-code-report.html
此时的扫描结果如下:

对比上面的四种扫描结果可以发现,SonarQube自带的规则对代码Bug的检查效果最好,DependencyCheck+FindBugs Security Audit对代码安全性检查效果最好。
SonarQube集成到CI&CD中



 
使用Giltab-CI集成SonarScanner
PS.注意SonarScannerdependency-check的权限(在执行pipeline时可以查看详情来观察是否出现异常)
stages:  - sast
sast: stage:sast script: - mvn clean package -DskipTests # 编译项目 - mkdir $PWD/reports # 创建报告目录 - /home/gitlab-runner/dependency-check/bin/dependency-check.sh -s $PWD/ -f XML -o $PWD/reports/dependency-check-report.xml # 执行扫描,并生成xml报告 - /home/gitlab-runner/dependency-check/bin/dependency-check.sh -s $PWD/ -f HTML -o $PWD/reports/dependency-check-report.html # 执行扫描,并生成html报告 - sonar-scanner -Dsonar.host.url=http://10.10.10.12:9000 -Dsonar.login=token -Dsonar.projectKey=$CI_PROJECT_NAME:$CI_COMMIT_REF_NAME -Dsonar.java.binaries=$PWD -Dsonar.dependencyCheck.reportPath=$PWD/reports/dependency-check-report.xml -Dsonar.dependencyCheck.htmlReportPath=$PWD/reports/dependency-check-report.html # 执行扫描,并将报告传输给SonarQube tags: - security
 
关于CI&CD集成代码扫描的思考
如果每次提交代码都需要进行依赖包扫描则会出现非常浪费时间的情况(因为打包是一个非常耗时的过程)。所以推荐是在发布到测试环境或预生产环境时的Jenkins pipeline中使用依赖包扫描。
并且推荐定期更新DependencyCheck的漏洞库,最好选择在周末的夜间执行定时任务,定期更新的周期不应该超过7 ,因为间隔不超过7天只需要维护一个很小的XML列表,时间消耗大概只会在一分钟之内。
至于普通的代码扫描,可以在Gitlab合并代码分支时进行。
 
对于扫描出来的代码如何进行闭环
在这里主要需要思考这么几个问题:
扫描完成后如何通知安全人员或者研发人员?
关于通知相关人员,重要的是知道什么项目通知哪些人。可以使用构建对应关系(project -- username)来映射,可以在pipeline的代码扫描流程之后添加一个钉钉或邮件通知的过程即可。在这里小B通过一段代码将SonarQube扫描结果中严重、高危、中危级别的漏洞提取出来发送给安全人员(http://172.16.26.13:9000/web_api/)
import requests
def get_vul(project_name): url = "http://172.16.26.13:9000/api/issues/search?project=%s&types=VULNERABILITY" % project_name vulnerability_number = requests.get(url,auth=('admin','admin')).json()['paging']['total']
for row in requests.get(url,auth=('admin','admin')).json()['issues']: if row['status'] == "OPEN": if row['severity'] == "MAJOR" or row['severity'] == "CRITICAL" or row['severity'] == "BLOCKER": alarm_data = "" else: pass else: pass
print("漏洞数量: ", vulnerability_number)

if __name__ == '__main__': project_name = "java-sec-code_dependency" get_vul(project_name)

扫描出来的问题的准确性
是工具就会有误报与漏报,所以很多时候直接将结果推送给研发人员是不靠谱的选择,需要安全人员初步过一遍以后再发送到业务方进行处理。所以小B对上一步脚本的提取结果进行复查,如果确认漏洞存在相应的危害性再将漏洞提交到企业自身的漏洞管理平台
扫描出来的问题应该如何进行修复?
处于漏洞管理平台中的漏洞根据漏洞生命周期要求推动修复。
SonarQube在安装使用中的一些问题



 
项目私有化处理
SonarQube默认项目为公有,此时不需要登录就可以查看SonarQube中的项目,为了避免信息泄露问题,推荐将项目属性修改为默认私有

 
SonarQube如何使用其他类型数据库
  • 使用阿里云RDS,新版本不支持MySQL了可以使用阿里云的PostgreSQL

sonar.jdbc.url=jdbc:mysql://address:3306/sonar?useUnicode=true&characterEncoding=utf8
 
不允许从数据库中直接删除规则
不要从数据库中直接删除规则,否则会报错如下:
ERROR: Unable to load component class org.sonar.scanner.report.MetadataPublisherERROR: Caused by: Unable to load component class org.sonar.scanner.rule.QualityProfiles
 
sonar.projectKey命名
在普通使用SonarScanner过程中,我们可以自定义sonar.projectKey的值,但是如果把SonarScanner集成到CI&CD流程中时,我们会使用预定义变量来命名,通常我们会使
CI_COMMIT_REF_NAME,但是如果使用这个变量在项目名称中包含/#等特殊字符时,Gitlab-CIpipeline会执行失败。这个时候我们选择CI_COMMIT_REF_SLUG(先将$CI_COMMIT_REF_NAME的值转换成小写,最大不能超过63个字节,然后把除了0-9a-z的其他字符转换成-。在URLs和域名名称中使用。)
GitLab CI/CD Variables中文文档:http://www.ttlsa.com/auto/gitlab-cicd-variables-zh-document/
refer



阅读原文

跳转微信打开