作者:MyKings
作者博客:http://mykings.me/
代碼審計一直是企業白盒測試的重要一環,面對市場上眾多商業與開源的審計工具,你是否想過集眾家之所長來搭建一套自動化的掃描系統呢?也許這篇文章會給你一些思路:)
一、背景
1. 為什么需要自動化掃描?
互聯網的快速發展,程序員是功不可沒的。從軟件開發的瀑布模型到現在的敏捷開發, 軟件的開發周期從數年到數月、從數月到數天,時間不斷變換縮減。傳統的代碼掃描方式已經不能跟進新時代的軟件開發流程中,這就需要改變我們的代碼掃描方式,它應該在有限的時間內盡量發現足夠多的安全問題,并能夠結合 CI (持續集成) 來觸發代碼掃描。
2. 自動化掃描時掃描引擎用什么?
你可以使用任何提供 API 接口的開源或商業的掃描引擎。這里我們把掃描引擎(指第三方的審計工具)與自動化系統剝離(解耦)開來,掃描引擎只負責安全漏洞掃描;自動化系統負責漏洞收集、分析、處理等動作。同時你也可以通過自動化系統的后臺來添加一些安全規則。
3. 怎么觸發自動化掃描?
兩種方式:
- 管理后臺手動觸發
- CI(持續集成)/CD(持續交付) 系統中觸發
4. 如何解決漏報和誤報?
兩種方式:基于規則和基于插件,這里使用白名單表示誤報、使用黑名單表示漏報。
基于規則
這里的規則是指基于文本的處理方式,如:使用正則表達式來匹配文件中的某些特征,使用字符串來判斷是否存在敏感信息等。
PS: 這里有一些細節需要注意。
a. 正則表達式:
- 是否區分大小寫
- 是否支持多行匹配
b. 字符串:
- 是否首部包含
- 是否尾部包含
- 是否在當前字符串中
基于插件
插件的自由度較大,其本質是把要掃描的文件信息作為插件執行入口的上下文,文件信息包括:路徑、名稱、內容等。你也可以在插件中寫一些判斷邏輯或調用第三方工具。
5. 漏報率和誤報率怎么樣?
漏報率暫時無法很好的測量, 漏報率 = 漏報數/(漏洞數+漏報數) × 100% 而實際中很少有人(這里指非安全審計人員)會發現漏報,理論上你寫的規則或插件越多越會減少漏報。
誤報是可以通過白名單規則或插件處理的,理論上可以百分百消除誤報,但是需要手動進行設置。
6. Web 通用型漏洞可以都覆蓋么?
Web 通用型漏洞以 OWASP 2017 TOP 10 為例,其大多數都在黑盒測試的范圍內。適用白盒測試僅限于:A3:2017-Sensitive Data Exposure、A9:2017-Using Components with Known Vulnerabilities,而這兩項也是我們自動化掃描系統的基本要求。
7. 掃描出來的漏洞如何形成閉合?
這需要結合公司自身的軟件開發方式而定,大多數公司采用Gitlab + Confluence + Jira + Jenkins的工作方式,那么我們可以通過 Jira 或 Gitlab 的 API 接口來創建問題工單。
二、設計要求
1. 目標
系統功能:
- 盡量發現足夠多的安全問題
- 硬編碼問題
- 敏感信息泄露
- 使用存在已知漏洞的組件
-
危險函數識別
-
可集成第三方掃描引擎
- 可自動化處理誤報
- 可通過 CI 方式觸發掃描
- 可根據條件自動創建 Issue
掃描目標主要分為兩種:一種為線上 Git 掃描;一種為離線掃描。線上 Git 掃描其主要應用場景為企業內部使用如 Gitlab 這種代碼托管系統,我們定時同步 Gitlab 上的項目信息,通過 CI 來調用 API 接口進行掃描,自動化掃描就是這種模式,其執行流程為:“后臺服務定時同步項目” -> "API接口下發掃描任務" -> “后臺調度執行掃描”。離線掃描其形式為審計人員手動上傳一個 zip 或 rar 的源碼包,掃描系統自動解壓后進行代碼掃描。
這兩種模式的執行流程略有不同,所以后端實現也略有不同,第一種的執行流程要比第二種較為復雜,我們這里會以第一種方式來實現自動化掃描。
2. 要求
系統要求:
- 單個項目掃描控制在 20 分鐘以內
- 支持調度節點監控
- 支持漏洞知識庫管理
- 支持 API 接口
- 支持分布式部署, 方便擴展來提升掃描能力
時間控制是為了保證 CI/CD 過程不會太長,避免影響項目發布。調度節點監控,這里存在兩種情況:一種是調度進程的心跳監控;一種為掃描任務的超時監控。漏洞知識庫主要為了與組件分析模塊分析出的依賴組件進行漏洞匹配。API 接口是為了方便第三方(代指CI/CD)調用來下發掃描任務或查詢結果。分布式部署方式,可以水平擴展來提高調度節點和API節點的處理速度與能力。
3. 模塊設計
主要系統:
- 代碼托管子系統
- 自動化掃描子系統
- 第三方掃描子引擎
這里從整體角度來觀察,大致分為三個子系統,自動化掃描子系統依賴代碼托管子系統,但不依賴掃描引擎(泛指第三方商業或開源審計產品)子系統,這是由于自動化掃描子系統內部集成了組件漏洞識別、黑白名單規則、黑白名單插件等功能,最后我們用一張圖來說明。

三、小結
代碼掃描固然重要,但是它不會解決所有項目的安全問題。項目安全應該從多個維度、多角度的來進行。如:項目立項時開始SDL;開發迭代過程中的代碼掃描;項目上線前的黑盒測試。
四、系統設計
4.1 基礎與準備
這里我們主要使用 Linux 來搭建我們的自動化掃描系統,按照設計的角色劃分,我們這里需要三臺 CentOS 7 的服務器,當然服務器可以是物理設備也可以是虛擬機,如果公司內部的掃描項目較多或為以后擴展考慮意見使用物理機。

服務器劃分:
這里我們假定一個 codeaudit 域, 三臺服務器的主機名稱分別為:
- ui.codeaudit: 負責后臺管理系統的部署,包括數據庫、MQ。
- task.codeaudit: 負責調度掃描引擎。
- sonarqube.codeauit: SonarQube的后臺服務端。
4.2 技術說明
這里會討論到所需的具體技術點,有些技術或方法可能不是最佳的方案,但是已經過我們測試檢驗是可行的。
以下為實際開發中用到的一些技能:
- Python/Django/Jquery/Celery
- Gitlab API/Sonar API
- Git/Gitlab CI/Jenkins
- Centos 7/Shell
- NFS/Nginx/uWSGI
- MySQL/RabbitMQ/Redis
- 安全漏洞知識
CentOS 7
CentOS 7 與 6 的版本會有一些區別,我們需要具有 Linux 的基本操作基礎,了解 systemctl、firewall-cmd、crontab等命令;了解SELinux,修改SELinux狀態;并能編寫systemd的自啟動腳本。
Git
了解 Git 的基本操作命令,使用 SSH 密鑰的方式提交或拉取代碼; 熟悉git clone、git log、git pull、git branch、git remote、git fetch、git for-each-ref、git ls-files等基本命令的操作。
例如:
- 使用
git for-each-ref來得到當前分支的最后一次 commit id; - 使用
git ls-files來判斷項目中是否存在sonar-project.properties配置文件; - 使用
git log -n1 /path/file來獲取文件最后一次 commit 的作者。
CI/CD
不論是集成到 Gitlab CI 或是Jenkins, 我們都需要先了解項目上線的基本流程,如:開發的代碼規范、測試環節(單元測試/功能測試)、發布部署環節等。一般我們會將代碼掃描環節放在在測試環節后,發布部署前。
Python
這里我們使用Python進行后臺的服務端開發,使用Django進行前臺 UI 的開發,使用django-rest-swagger來開發 API 接口,使用Celery作為掃描任務的調度框架。
數據庫與中間件
數據庫我們選擇使用 MySQL 5.7(或MariaDB, 他們在使用上沒有太大的區別); Celery 的消息中間件可以使用 Redis 或是 RabbitMQ,這里你可以在開發的時候使用 Redis,正式部署時使用 RabbitMQ。
安全漏洞知識
- 了解 OWASP TOP 10 的漏洞類型原理與解決方案;
- 了解 CWE 的漏洞信息;
- 了解公司主流的開發語言。
4.3 模塊設計
下圖中我們自上而下按照邏輯大致劃分了"四"層:UI層、存儲層、服務層、任務調度掃描引擎層(由于任務調度與服務同在后臺運行,所以又統稱為服務層)。

4.3.1 UI 層
提供掃描系統的后臺管理、API接口、漏洞知識庫等一系列的交互功能入口,不同的人員或系統可以根據各自的需求通過不同的交互接口來滿足自己需求。如:CI/CD系統可通過 API 接口創建掃描任務并獲取掃描結果;安全審計人員可通過后臺進行規則或插件的添加;開發人員可通過漏洞知識庫來獲取相關語言或技術的漏洞信息。
4.3.2 存儲層
主要包括關系型數據庫、消息中間件(指MQ)、NFS(網絡文件系統),這里我們使用了 MySQL 5.7 的數據庫; RabbitMQ 是作為 Celery 調度框架的消息中間件;NFS擔當網絡共享存儲,用于存儲代碼與掃描日志。
4.3.3 調度層
掃描任務的執行流程,主要可分為:
- 初始化:掃描任務的環境初始化,如:日志目錄、日志文件、加載插件、加載漏洞規則等;
- 分析項目:項目代碼統計、依賴組件統計、漏洞知識庫關聯等;
- 掃描漏洞:調用第三方掃描引擎、統計掃描結果;
- 漏報處理:使用黑名單規則和插件進行掃描;
- 誤報處理:使用白名單規則和插件進行誤報處理;
- 閉環漏洞:針對高危漏洞在 GitLab 或 Jira 系統中創建一個 Issue。
4.3.4 服務層
后臺的服務,其主要包括:GitLab 系統中的項目同步、報表生成、調度進程監控。
五 系統功能
5.1 數據庫設計
5.1.1 權限相關
權限控制,這里使用 django 自帶的權限表來進行權限控制,我們可以通過auth_group表來創建用戶組,為不同的用戶組賦予不同的角色權限auth_group_permissions,你可以訪問官方地址:https://docs.djangoproject.com/en/2.1/topics/auth/default/#topic-authorization
來獲得更多關于權限的信息。
django 權限表如下:
auth_groupauth_group_permissionsauth_permissionauth_userauth_user_groupsauth_user_user_permissions

5.1.2 項目相關
項目表主要包括:項目組、項目、分支與TAG、統計信息、依賴組件、插件規則、掃描任務等相關表。

5.1.3 漏洞知識庫
漏洞知識庫,這里主要存儲漏洞類型、漏洞知識等內容。

5.1.4 系統相關
系統表主要包括系統的安全周報、節點監控、系統日志等信息。

5.2 UI系統
掃描系統的后臺,方便安全審計人員管理項目和系統。
5.2.1 項目管理
5.2.1.1 項目組
項目組我們通過 GitLab 的 API 同步所有項目組信息到我們的掃描系統,項目組的信息包括:項目組名稱、項目組描述、創建時間、URL地址、項目成員等。
5.2.1.2 項目
項目是從分組中獲取得到,需要注意的是可能會存在項目名相同但分組不同的情況。項目基本信息應包括:項目名稱、項目描述、所屬分組、默認分支、Git地址、項目成員、代碼統計、依賴組件、分支管理、TAG管理等。

5.2.1.3 掃描任務
掃描任務會有四種狀態:等待調度、正在掃描、掃描完成、掃描失敗。每一次創建掃描任務時,都會查詢是否存在等待調度或正在掃描的任務,如果存在則提示創建失敗。

5.2.2 規則插件
5.2.2.1 規則
這里使用正則表達式來做特征匹配,并可通過限定文件的后綴來提高精準度。
正則表達式標志位:
- 忽略大小寫
- 支持多行匹配

5.2.2.2 插件
這里使用了 Python 的反射機制,任務初始化時會優先初始化插件,當掃描完成時,掃描引擎會使用插件批量進行檢測。插件入口函數為run(),漏洞詳情對象會作為**kwargs參數的上下文傳到該函數中。

5.2.2.3 規則知識庫
規則知識庫是區別與漏洞知識庫的,往往規則知識庫的內容要比漏洞知識庫的內容簡單,但是結構清晰。如:漏洞示例代碼、漏洞說明、解決辦法、參考鏈接等信息。

5.2.3 漏洞知識庫
5.2.3.1 漏洞類型
這里建議使用 CWE 的漏洞標準,可參考這個文檔:https://www.hackerone.com/sites/default/files/2017-03/WeaknessAndLegacyVulnerabilityTypeRelationship.pdf

5.2.3.2 漏洞管理
主要包括添加漏洞和管理漏洞,漏洞的信息應該包括:CVE/CNVD/CNNVD編號、漏洞標題、風險等級、漏洞來源、發現時間、受影響范圍、漏洞詳情、漏洞類型、解決版本等基本信息。

這里我們要實現漏洞知識庫與識別出的組件聯動功能,主要通過兩個屬性:
- 組件標簽
這里需要為每個漏洞添加一個 Tag 屬性,其屬性值如:org.springframework、com.alibaba.fastjson,建議標簽一律使用小寫字母。
- 版本規則
使用正則表達式來進行匹配,如:CVE-2018-1270 中受影響的 Spring Framework 版本為:5.0.x-5.0.5 和 4.3.x-4.3.16,那么我們的規則可以寫成如下:
5\.0 ### 5.0
(5\.0\.[0-4]{1}) ### 5.0.x -5.0.5
(4\.3\.1[0-5]{1}) ### 4.3.1x.release
(4\.3\.[0-9]{1}\.) ### 4.3.x.release
5.2.4 報表管理
5.2.4.1 語言與項目統計
按照年份進行項目的語言統計。

5.2.4.2 周期性漏洞統計
每季度漏洞數對比
季度統計是為了對比同一段時期的漏洞數。

高危漏洞趨勢圖
高危漏洞環比,今天實施的安全政策是否合乎預期,可以大概分析出來。

5.3 API接口
5.3.1 接口認證
使用 rest_framework 的 API 來做驗證,首先根據登陸的用戶 id 生成一個 Token。
from rest_framework.authtoken.models import Token
def create_token(request, user_id=None):
if request.user.id != int(user_id):
return HttpResponseRedirect("/error/403")
try:
user = User.objects.get(id=user_id)
except Token.DoesNotExist:
token = Token.objects.create(user=user)
return HttpResponseRedirect("/users/{0}".format(user_id))
驗證接口使用說明,添加 Authorization 的認證 Token。

5.3.2 項目信息接口
信息同步
為什么需要信息同步?這是因為 GitLab 中的項目名稱可能不是最終上線的 APP 名稱(這里有些繞)。拿一個 Java 的項目舉例,該項目的 GitLab 地址為:http://git.companyname.com/A/cloud,那么這個Java的包名有可能是 com.companyname.cloud。
我們使用項目的 git 地址來同步信息,建議把 git 地址全部轉換為小寫。APP 的名稱(包名)可以 CI/CD 系統獲取或是通過配置文件的硬編碼方式來定義。

創建掃描
信息同步完成后,我們就可以根據 APP 名稱和 git 地址來創建一個掃描任務,請求參數參考如下:
- app_name: APP名稱(可選)
- module_name: 模塊名稱(可選)
- version: 當前版本(可選)
- git_url: git地址 (必選)
- branch_name: 分支名稱(必選)
5.3.3 任務信息接口
查詢掃描任務
根據項目的 git 地址、分支來查詢掃描任務,你也可以根據上一步創建掃描任務的 ID 來查詢掃描結果。
查詢任務漏洞列表
當掃描任務狀態為掃描完成/掃描失敗時,就可以根據任務 ID 來查詢掃描出的安全漏洞信息。
5.3.4 漏洞規則接口
查詢漏洞規則知識
通過漏洞信息中的漏洞規則 ID 或者 Key 來查詢相關的規則知識庫,該知識庫應當包括:漏洞原因、漏洞示例代碼、解決修復意見等。
5.4 后臺服務
5.4.1 gitlab 的信息同步
使用 crontab 每兩個小時遍歷一遍 GitLab 上的所有項目,并同步項目信息到掃描系統中。

5.4.2 報表生成服務
使用 crontab 每日凌晨12點生成,季度對比和年度的安全統計數據。
5.4.3 掃描進程監控
使用ps aux| grep codescan來查看進程是否存活,當然這種暴力方式不能檢測到進程的業務健康度的(比如:掃描任務卡死,狀態一直為:正在掃描)。
5.5 SonarQube 搭建
5.5.1 服務搭建
下載最新版本https://www.sonarqube.org/downloads/上傳到sonarqube.codeauit服務器上并解壓。進入到bin/linux-x86-64/目錄下,執行 sh ./sonar.sh start。 SonarQube 啟動成功后,使用瀏覽器打開http://192.168.10.3:9000, 輸入 admin/admin 即可正常訪問。
5.5.2 插件管理
SonarQube 6.4 版本登陸的后臺管理系統,選擇 "配置" -> "系統" -> "更新中心" , 選擇對應插件點擊 “Install” 進行安裝。
SonarQube 7.3 版本, “Administration” -> "Marketplace", 選擇對應插件點擊 “Install” 進行安裝。
5.6 引擎調度
程序部署在 “task.codeaudit” 服務器上,服務需要安裝 cloc 與 sonar-scanner 工具。
5.6.1 代碼同步
同步代碼分為以下幾個步驟:
克隆項目
這里可能會遇到一些坑,比如項目歷史比較久遠,完整克隆下來可能會達到上百M或G,我們這里可以使用--depth 1參數進行克隆下載。有的項目可能會存在不規范的情況,比如拿 git 當 svn 使用,每個版本創建一個目錄。
切換分支
根據掃描任務中的分支名稱 checkout 到指定分支。
更新代碼
針對已經克隆的項目進行 pull 操作,來同步 GitLab 上的項目更新代碼。
5.6.2 sonar-scanner 掃描
ssh 登錄到task.codeaudit服務器上,執行cd opt && wget https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.2.0.1227-linux.zip && unzip sonar-scanner-cli-3.2.0.1227-linux.zip來下載并解壓,執行成功后使用ln -s /opt/sonar-scanner-3.2.0.1227-linux/bin/sonar-scanner /usr/bin/sonar-scanner命令創建一個sonar-scanner 的軟連接。這里我們會使用sonar-scanner命令來進行項目的代碼掃描。
你也可以通過https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner來下載不同平臺的sonar-scanner-cli-3.2.0.1227-linux.zip。

5.6.3 代碼統計
使用cloc工具進行文件與代碼行數的統計, 這里你可能需要通過--exclude-ext、--exclude-dir參數來過濾一些無意義的文件,比如:字體、圖片、聲音、視頻等。舉個例子,過濾所有圖片后統計:cloc ./目標路徑 --exclude-ext=.jpg,.jpeg,.png,.bmp,.gif,ico。
5.6.4 項目組件分析
組件分析主要是針對如使用 Java 語言開發項目時使用 Maven 管理的 pom.xml 配置文件; Python 中的 requirements.txt 文件;JS 項目中的package.json文件做解析。這里我寫了一個clocwalk 工具可以分析項目的依賴組件,這個項目目前已經開源,你可以通過https://github.com/MyKings/clocwalk地址來獲取這個工具。
5.6.5 漏報處理
關于漏報問題,你可以根據自己企業 SRC 中的漏洞,總結出一套適合自己企業的黑名單規則;或者你可以添加一些 CWE 的漏洞規則,關于 CWE 的信息你可以訪問這個地址 https://cwe.mitre.org/data/index.html。
5.6.6 誤報處理
關于誤報問題可能會較多,比如掃描出單元測試或功能測試的硬編碼問題;比如變量參數String PARAM_NAME_PASSWORD="passwd_txt";問題。
以上的問題我們可以通過白名單插件處理,比如插件中對文件路徑和方法判斷是否存在 test 關鍵字,如果存在我們就認為這個是誤報。另外針對某些特殊類型的誤報,比如在 A 項目下才是誤報,其他項目就是漏洞的情況,我們可以設置這個項目的白名單漏洞 Case,其匹配規則條件為:項目名稱、漏洞文件、漏洞類型、漏洞所在行,當所有條件都同時滿足時,那么這個漏洞就會可以判斷為誤報。
5.6.7 漏洞閉環
當一個高/中危漏洞被發現并確認時,我們應該如何跟蹤這個漏洞的生命周期?往往安全人員會將一個漏洞提交到內部的 SOC 系統中,由于 SOC 系統沒有和項目開發的流程控制系統(如:Jira)沒有直接聯系,開發人員可能會忽視或忘記修復這個高危漏洞,如何避免這種情況?我們這里以 GitLab 舉例,當掃描系統掃描出高危漏洞時,系統會通過 GitLab 的 POST /projects/:id/issues API接口來自動創建一條 issue 并指派給當前項目的 master,項目負責人同時會收到一條郵件提醒,那么項目負責人可根據漏洞嚴重程度來安排項目的迭代計劃,這樣我們就把審計系統掃描出的漏洞與項目開發流程很好的結合起來了。
5.7 GitLab CI 觸發
當然也可以使用 Jenkins 來做 CI/CD 系統。我們這里開發了一個.code-audit.py觸發腳本, Jenkins 你也可以使用 Python 腳本或是開發 Jenkins 插件來達到觸發目的。
5.7.1 配置項目
這里需要了解 .gitlab-ci.yml 文件格式的編寫,下面是一個 Python 項目的配置。可以看出整個 CI 過程分為 4 個階段:build、test、codeaudit、deploy。 其中codeaudit是我們的代碼掃描階段,這里我們限制了只有 develop 的動作才會觸發掃描。

5.7.2 掃描腳本
觸發掃描腳本如下圖,其大體的執行流程如下:
- 獲取 GitLab CI 中關于項目的環境變量信息;
- 設定要攔截的漏洞級別,默認:中、高危漏洞不通過測試;
- 同步項目信息到掃描系統,如果失敗掃描代碼不通過;
- 創建掃描任務,如果失敗掃描代碼不通過;
- 異步查詢掃描結果,超時時間10分鐘,如果超時掃描代碼不通過;
- 掃描結果完成,統計是否存在預定義級別的漏洞,如果存在掃描代碼不通過。

下圖為整個 CI 過程的截圖。

下圖為代碼掃描失敗的反饋結果,圖中可以看出發現了一個漏洞。

六 總結
關于 “自動代碼審計系統的建設” 文章這里就此完結了。下篇中有些章節可能說的比較籠統寬泛,但是要對每一個章節詳詳細細的說明,恐怕每一個章節都會寫下不止一篇文章了,本篇文章只是為大家提供一種思路,具體實施效果還是要靠大家自己來實踐總結,就這樣吧:)
參考鏈接
- CI (持續集成)
- CD (持續交付)
- OWASP 2017 TOP 10
- https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/crontab.html
- https://git-scm.com/docs
- https://docs.gitlab.com/ee/api/
- https://about.gitlab.com/features/gitlab-ci-cd/
- https://docs.gitlab.com/ce/ci/yaml/README.html
- https://docs.gitlab.com/ce/ci/examples/README.html
- https://docs.gitlab.com/ee/api/issues.html
- https://docs.gitlab.com/runner/install/docker.html
- https://docs.sonarqube.org/display/DEV/Web+API
- https://docs.djangoproject.com/en/2.1/topics/auth/default/#topic-authorization
- https://www.sonarqube.org/downloads/
- https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner
- https://www.hackerone.com/sites/default/files/2017-03/WeaknessAndLegacyVulnerabilityTypeRelationship.pdf
- https://github.com/MyKings/clocwalk
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/694/