近期打算做一個插件化的白盒靜態代碼安全審計自動化平台和黑盒網站安全審計自動化平台。現在開源或半開源做黑盒網站安全掃描的平台,大多是基於python腳本,安全人員貢獻python腳本插件增強平台功能。對自己或身邊開發人員,對java語言更熟悉,為了后期維護打算采用java寫一個這樣的平台。另外白盒代碼安全掃描也有Fortify等收費軟件,或依賴PMD做代碼分析,不過比如新增了什么安全問題,需要自定義或擴展就比較麻煩。
比如一個簡單的:現在用struts2存在漏洞,現在需要升級到2.3.28版本,於是我寫一個插件,判斷pom.xml里如果有配置struts2,只要版本不是2.8.2就認為存在漏洞。平台提供下載制定SVN或GIT或其他服務器的代碼,然后執行該插件即可掃描哪些項目有問題了。
該項目最近幾天和同事一起做了一個初步版本,想法還有不完善的地方,作為后期參考,主要目的是先搭建一個能用的架子出來。
首先提供插件,那就需要有一個獨立的jar項目作為插件依賴的接口項目,它需要定義插件的接口和交互的對象,接口需要至少2個方法:驗證該插件是否可掃描,執行插件邏輯掃描,比如白盒掃描插件接口定義如下:
public interface IWhiteboxService { /** * 驗證該文件是否符合規則需求 * * @param inputFile * 被掃描的文件信息 * @return 驗證結果 */ ValidateResult validate(InputFile inputFile); /** * 如果validate驗證通過,則開始掃描 * * @param inputFile * 被掃描的文件信息 * @return 掃描結果 */ List<ScanResult> scan(InputFile inputFile); }
然后需要有一個統一的服務,用於調用插件執行代碼掃描。通過網站或定時任務只需要調用該服務即可。后面可以做掃描任務只掃描哪些插件,比如掃描所有插件代碼:
public void scan(Whitetask task) { //todo:循環插件掃描。或后期根據任務和插件關閉表掃描 if (null == task || StringUtil.isNull(task.getCodepath())) { //todo:記錄日志,任務表有問題 return; } /* * 1、讀取whiteplugin表所有插件並緩存插件對應的代碼,不用每次反射 * 2、循環task.getCodepath()路徑下所有文件,默認java文件,根據getFileext()的文件類型和getExcluderule()排除的文件而定 * 根據循環后的文件存儲到LIST集合里,循環時排除文件是為了減少LIST的內容 * 3、根據循環LIST結果再循環所有文件,調用插件的代碼進行掃描 * 4、掃描結果插入whitescanresult表 * 5、循環完畢后,修改whitetask表的當前任務掃描完畢 */ List<Whiteplugin> allPlugins = whitepluginService.getAll(); if (null == allPlugins || allPlugins.size() < 1) { //todo:記錄日志,沒有可用的插件 return; } List<WhitePluginData> allPluginDatas = null; if (null != allPlugins) { allPluginDatas = WhitePluginGenerator.generate(allPlugins); } if (null == allPluginDatas || allPluginDatas.size() < 1) { //todo:記錄日志,有插件,轉換失敗 return; } List<FileData> fileDatas = FileGenerator.generate(task.getCodepath(), task.getFileext(), task.getExcluderule()); doScan(fileDatas, allPluginDatas, task); }
真正掃描的doScan方法:
public void doScan(List<FileData> fileDatas, List<WhitePluginData> allPluginDatas, Whitetask task) { if (null == fileDatas || null == allPluginDatas || fileDatas.size() < 1 || allPluginDatas.size() < 1) { return; } for (int i = 0; i < fileDatas.size(); i++) { //循環每個文件調用每個插件 FileData tempFileData = fileDatas.get(i); for (int j = 0; j < allPluginDatas.size(); j++) { //循環每個插件 IWhiteboxService whiteboxService = allPluginDatas.get(j).getWhiteboxService(); InputFile inputFile = new InputFile(); inputFile.setContents(tempFileData.getContent()); inputFile.setFileExt(FileUtil.getEXT(tempFileData.getFileName())); inputFile.setFileName(tempFileData.getFileName()); //todo:這里傳遞的是絕對路徑,不是包路徑 inputFile.setPackagePath(tempFileData.getPath()); List<InputFileContent> inputFileContents = getFileContents(tempFileData.getContent()); inputFile.setFileContents(inputFileContents); ValidateResult validateResult = whiteboxService.validate(inputFile); if (null != validateResult && validateResult.isIssuccess()) { List<ScanResult> scanResults = whiteboxService.scan(inputFile); if (null != scanResults && scanResults.size() > 0) { //插入數據庫 for (int z = 0; z < scanResults.size(); z++) { Whitescanresult whitescanresult = getScanResult(inputFile, scanResults.get(z), allPluginDatas.get(j), task); whiteScanResultService.save(whitescanresult); } } } } } }
根據插件掃描完成后,把結果插入掃描結果表。核心的表結構包括任務表、插件表、掃描結果表,相關主要表字段如下:
DROP TABLE IF EXISTS `vulfeel`.`whiteplugin`; CREATE TABLE `vulfeel`.`whiteplugin` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(200) DEFAULT '' COMMENT '插件名稱', `type` tinyint(4) DEFAULT NULL COMMENT '后期擴展分類', `importlevel` tinyint(4) DEFAULT NULL COMMENT '插件重要性,1輕微,2一般,3重要', `classpath` varchar(2000) DEFAULT NULL COMMENT '插件包路徑,比如com.xx.vulfeel.plugin.a', `filename` varchar(200) DEFAULT NULL COMMENT 'class名稱或jar名稱,如果插件是jar包則是jar包名稱,比如a.jar;否則是class名稱,比如a.class', `functions` varchar(2000) DEFAULT NULL COMMENT '插件功能描述', `version` varchar(200) DEFAULT NULL COMMENT '插件版本', `refurl` varchar(1000) DEFAULT NULL COMMENT '插件原理參考URL等內容', `userid` int(11) DEFAULT NULL COMMENT '插件提供者用戶ID', `createtime` datetime DEFAULT NULL, `remark` varchar(2000) DEFAULT NULL COMMENT '插件其他備注', PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='白盒掃描插件表'; DROP TABLE IF EXISTS `vulfeel`.`whitescanresult`; CREATE TABLE `vulfeel`.`whitescanresult` ( `id` int(11) NOT NULL AUTO_INCREMENT, `taskid` int(11) DEFAULT NULL COMMENT '關聯任務表ID', `pluginid` int(11) DEFAULT NULL COMMENT '關聯問題插件ID', `impactlevel` tinyint(4) DEFAULT NULL COMMENT '影響程度,1輕微,2一般,3重要,主要和插件表的importlevel關聯', `classpath` varchar(2000) DEFAULT NULL COMMENT '插件包路徑,比如com.xx.vulfeel.plugin.a', `filename` varchar(200) DEFAULT NULL COMMENT 'class名稱或jar名稱,如果插件是jar包則是jar包名稱,比如a.jar;否則是class名稱,比如a.class', `startline` int(11) DEFAULT NULL COMMENT '問題開始代碼行', `stopline` int(11) DEFAULT NULL COMMENT '問題結束代碼行', `createtime` datetime DEFAULT NULL, `remark` varchar(2000) DEFAULT NULL COMMENT '其他備注', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='白盒掃描結果表'; DROP TABLE IF EXISTS `vulfeel`.`whitetask`; CREATE TABLE `vulfeel`.`whitetask` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(200) DEFAULT NULL COMMENT '任務名稱', `codepath` varchar(1000) DEFAULT NULL COMMENT '代碼路徑,不依賴SVN還是GIT等,只需要傳入代碼地址即可', `fileext` varchar(200) DEFAULT NULL COMMENT '掃描的文件格式,默認java文件,逗號分隔,比如:java,js', `excluderule` varchar(1000) DEFAULT NULL COMMENT '不掃描的路徑規則,::兩個冒號分割正則,比如:.*test::js$::css$', `status` tinyint(4) DEFAULT NULL COMMENT '任務狀態,1初始任務,2正在執行,3任務正常結束,4任務異常結束', `userid` int(11) DEFAULT NULL COMMENT '關聯創建任務的用戶ID', `createtime` datetime DEFAULT NULL, `lastmodifytime` datetime DEFAULT NULL, `remark` varchar(2000) DEFAULT NULL COMMENT '其他備注', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='白盒掃描任務';
建立任務后,只需要點擊執行掃描,即可根據插件掃描出結果,查看結果:
比如一個簡單的掃描sql注入的插件掃描結果如上,插件代碼如下:
public class SqlInjectPlugin implements IWhiteboxService { @Override public ValidateResult validate(InputFile inputFile) { // TODO Auto-generated method stub ValidateResult result = new ValidateResult(); result.setIssuccess(false); if (null == inputFile || null == inputFile.getFileContents()) { return result; } if (inputFile.getPackagePath().contains("dao")) { result.setIssuccess(true); } return result; } @Override public List<ScanResult> scan(InputFile inputFile) { // TODO Auto-generated method stub List<ScanResult> result = new ArrayList<ScanResult>(); ScanResult scanResult = new ScanResult(); if (null == inputFile || null == inputFile.getFileContents()) { return result; } Pattern pattern = Pattern.compile("(\"[^\"]+\"|\\w+)\\s*(\\+|\\+=)\\s*\\w+"); if (inputFile.getPackagePath().contains("dao")) { //只掃描包路徑有dao的文件 //循環inputFile的每一行,如果有拼接sql就說明存在注入 for (int i = 0; i < inputFile.getFileContents().size(); i++) { InputFileContent inputFileContent = inputFile.getFileContents().get(i); Matcher matcher = pattern.matcher(inputFileContent.getContent()); if (matcher.find()) { scanResult.setStartline(inputFileContent.getLine()); scanResult.setStopline(inputFileContent.getLine()); scanResult.setRemark("存在sql拼接!"); result.add(scanResult); } } } return result; } }
現在項目主要是搭建一個架子,web黑盒掃描的架子也類試,白盒掃描現在是靜態代碼掃描,沒有用PMD等對代碼生成語法樹分析等,還非常簡單。后面有時間再完善和擴展。
如需要轉載請注明來自:http://lawson.cnblogs.com/