JAVA语言搭建白盒静态代码、黑盒网站插件式自动化安全审计平台

近期打算做一个插件化的白盒静态代码安全审计自动化平台和黑盒网站安全审计自动化平台。现在开源或半开源做黑盒网站安全扫描的平台,大多是基于python脚本,安全人员贡献python脚本插件增强平台功能。对自己或身边开发人员,对java语言更熟悉,为了后期维护打算采用java写一个这样的平台。另外白盒代码安全扫描也有Fortify等收费软件,或依赖PMD做代码分析,不过比如新增了什么安全问题,需要自定义或扩展就比较麻烦。

比如一个简单的:现在用struts2存在漏洞,现在需要升级到2.3.28版本,于是我写一个插件,判断pom.xml里如果有配置struts2,只要版本不是2.8.2就认为存在漏洞。平台提供下载制定SVN或GIT或其他服务器的代码,然后执行该插件即可扫描哪些项目有问题了。

该项目最近几天和同事一起做了一个初步版本,想法还有不完善的地方,作为后期参考,主要目的是先搭建一个能用的架子出来。

首先提供插件,那就需要有一个独立的jar项目作为插件依赖的接口项目,它需要定义插件的接口和交互的对象,接口需要至少2个方法:验证该插件是否可扫描,执行插件逻辑扫描,比如白盒扫描插件接口定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public interface IWhiteboxService {
    /**
     * 验证该文件是否符合规则需求
     *
     * @param inputFile
     *            被扫描的文件信息
     * @return 验证结果
     */
    ValidateResult validate(InputFile inputFile);
 
    /**
     * 如果validate验证通过,则开始扫描
     *
     * @param inputFile
     *            被扫描的文件信息
     * @return 扫描结果
     */
    List<ScanResult> scan(InputFile inputFile);
}

  然后需要有一个统一的服务,用于调用插件执行代码扫描。通过网站或定时任务只需要调用该服务即可。后面可以做扫描任务只扫描哪些插件,比如扫描所有插件代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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);
                    }
                }
            }
        }
    }
}

  根据插件扫描完成后,把结果插入扫描结果表。核心的表结构包括任务表、插件表、扫描结果表,相关主要表字段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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注入的插件扫描结果如上,插件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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/

 

posted @   Lawson  阅读(2263)  评论(1编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示