【BurpSuite 插件开发】实战篇(十五)实现插件配置保存与恢复功能

前言

在安全测试工作中,插件工具的配置管理往往直接影响测试效率与准确性。无论是临时请求头的定制、规则列表的筛选逻辑,还是各类复选框的状态设置,都需要测试人员根据场景反复调整。然而,手动重复配置不仅耗时,更可能因操作疏忽导致测试环境不一致,进而影响结果可信度。尤其在团队协作场景中,如何快速共享配置、复现测试环境,成为提升工作流效率的关键问题。

基于此,我在Burp插件的“保存/恢复”标签页中新增了配置保存与恢复功能。该功能允许测试人员将当前所有配置(包括临时请求头、复选框状态、规则列表等核心信息)导出为JSON文件,也可通过本地文件快速恢复配置,从而实现“一次配置,多次复用”的效果。本文将详细解析该功能的实现逻辑,帮助安全测试人员理解其原理与使用价值。

实现的插件效果如下,文章结尾查看完整代码。
在这里插入图片描述

一、功能需求与设计思路

1.1 核心功能需求

根据安全测试场景的实际需求,该功能需实现以下目标:

  • 在插件面板“保存/恢复”标签页中,新增UI元素:分割线、“配置状态:”标签、“保存”和“恢复”按钮;
  • “保存”按钮:将当前配置中心的所有设置(临时请求头、复选框状态、规则列表等)保存为本地JSON文件;
  • “恢复”按钮:读取本地JSON配置文件,将配置还原到插件面板中并生效;
  • 配置内容需包含:
    • 临时请求头(Base64编码,含换行符);
    • 4个复选框状态(“检查未授权”“忽略请求响应码204|304”等);
    • 规则列表的逻辑关系(And/Or);
    • 配置规则(Base64编码,含规则类型与内容);
    • 保存/恢复相关设置(文件类型、导出过滤下拉框选项,“去除重复项”复选框状态)。

1.2 设计思路

为实现上述需求,我们采用“UI布局扩展+工具类封装+配置序列化/反序列化”的方案:

  • UI层:通过Swing组件扩展现有面板,添加所需元素并绑定按钮事件;
  • 工具类:封装ConfigUtil工具类,集中实现配置的保存(序列化)与恢复(反序列化)逻辑;
  • 数据处理:使用Base64编码处理含特殊字符的文本(如请求头、规则内容),避免JSON序列化异常;通过Gson库实现配置与JSON文件的转换,保证数据格式一致性。

二、代码实现解析

2.1 依赖添加:引入JSON处理库

配置的保存与恢复需要将内存中的配置数据与本地JSON文件互转,因此需引入Google的Gson库(一款高效的JSON处理工具)。在build.gradle.kts中添加依赖:

dependencies {
    implementation("com.google.code.gson:gson:2.13.1")
}

解析:Gson库支持将Java对象(如Map、自定义类)直接序列化为JSON字符串,也可将JSON字符串反序列化为对象,大幅简化了文件读写逻辑。选择2.13.1版本为当前最新版本。

2.2 UI布局扩展:添加交互元素

为在现有“保存/恢复”标签页中新增元素,需修改面板初始化方法initializeComponents,添加分割线、标签和按钮。

2.2.1 新增分割线与标签

// 添加分割线
JSeparator separator = new JSeparator(SwingConstants.HORIZONTAL);
separator.setAlignmentX(Component.LEFT_ALIGNMENT);
saveRestoreSettingsPanel.add(separator);

// 添加配置状态标签
JPanel configStatusPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
configStatusPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
configStatusPanel.add(new JLabel("配置状态:"));
saveRestoreSettingsPanel.add(configStatusPanel);

解析

  • 分割线(JSeparator)用于视觉上区分“导出报告”按钮与新增功能区域,提升UI层次感;
  • “配置状态:”标签(JLabel)用于提示后续按钮的功能范围,帮助测试人员快速理解交互目标;
  • 使用FlowLayout.LEFT布局,确保元素左对齐,符合常规UI阅读习惯。

2.2.2 新增“保存”与“恢复”按钮

// 添加保存和恢复按钮
JPanel configButtonPanel = getConfigButtonPanel();
saveRestoreSettingsPanel.add(configButtonPanel);

private JPanel getConfigButtonPanel() {
    JPanel configButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
    configButtonPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
    JButton saveConfigButton = new JButton("保存");
    saveConfigButton.addActionListener(e -> ConfigUtil.saveConfiguration(this, permissionTestPanel, fileTypeComboBox, exportFilterComboBox, removeDuplicatesCheckBox));
    JButton restoreConfigButton = new JButton("恢复");
    restoreConfigButton.addActionListener(e -> ConfigUtil.restoreConfiguration(this, permissionTestPanel, fileTypeComboBox, exportFilterComboBox, removeDuplicatesCheckBox));
    configButtonPanel.add(saveConfigButton);
    configButtonPanel.add(restoreConfigButton);
    return configButtonPanel;
}

解析

  • 按钮面板(configButtonPanel)采用左对齐流式布局,确保“保存”“恢复”按钮紧凑排列;
  • 按钮点击事件通过addActionListener绑定到ConfigUtil的静态方法,将核心逻辑与UI层解耦,便于后续维护;
  • 方法参数传递了面板实例(permissionTestPanel)、下拉框(fileTypeComboBox等)和复选框(removeDuplicatesCheckBox),确保工具类能获取当前所有配置数据。

2.3 核心工具类:ConfigUtil的实现

ConfigUtil是功能实现的核心,封装了配置的保存(saveConfiguration)与恢复(restoreConfiguration)方法,负责数据的采集、编码、序列化与反序列化。

2.3.1 保存配置:saveConfiguration方法

该方法的核心流程为:打开文件选择器→采集当前配置数据→编码与封装→序列化并写入JSON文件。

步骤1:文件选择与校验

JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("保存配置文件");
fileChooser.setSelectedFile(new File("PermissionTest_Config.json"));

int userSelection = fileChooser.showSaveDialog(parent);

if (userSelection == JFileChooser.APPROVE_OPTION) {
    File fileToSave = fileChooser.getSelectedFile();
    String filePath = fileToSave.getAbsolutePath();
    
    // 确保文件有.json扩展名
    if (!filePath.endsWith(".json")) {
        filePath += ".json";
        fileToSave = new File(filePath);
    }
    // ...后续逻辑
}

解析

  • 使用JFileChooser提供可视化文件选择界面,默认文件名设为PermissionTest_Config.json,便于用户识别;
  • 强制添加.json扩展名,避免因用户输入错误导致文件格式异常,保证后续恢复时能正确解析。

步骤2:配置数据采集与编码

配置数据分为“配置中心”和“保存/恢复”两部分,需分别采集并封装为Map结构。

(1)配置中心数据采集

// 保存配置中心的设置
Map<String, Object> configCenter = new HashMap<>();

// 临时请求头:Base64编码
String tempHeaders = permissionTestPanel.getConfigPanel().getTempHeaders();
String encodedHeaders = Base64.getEncoder().encodeToString(tempHeaders.getBytes());
configCenter.put("tempHeaders", encodedHeaders);

解析

  • 临时请求头可能包含换行符、特殊字符(如:=),直接存入JSON可能导致格式错误;使用Base64编码将其转为纯文本,确保序列化时不破坏JSON结构。
// 复选框状态:JSON格式
Map<String, Boolean> checkBoxes = new HashMap<>();
checkBoxes.put("checkUnauthorized", permissionTestPanel.getConfigPanel().isUnauthorizedCheckSelected());
checkBoxes.put("ignore204304", permissionTestPanel.getConfigPanel().isIgnore204304Checked());
checkBoxes.put("avoid304", permissionTestPanel.getConfigPanel().isAvoid304Checked());
checkBoxes.put("useConfigRules", permissionTestPanel.getConfigPanel().isUseConfigRulesChecked());
configCenter.put("checkBoxes", checkBoxes);

解析

  • 4个复选框状态直接以Boolean类型存入Map,反映安全测试中的关键配置(如是否检查未授权、是否忽略特定响应码),确保恢复后测试逻辑与原配置一致。

(2)规则列表数据采集

规则列表包括低权限规则、未授权规则、拦截过滤器规则,需保存逻辑关系(And/Or)与规则内容(类型+内容)。

// 低权限规则面板设置
Map<String, Object> lowPrivilegeRules = new HashMap<>();
lowPrivilegeRules.put("logicRelation", permissionTestPanel.getConfigPanel().getLowPrivilegeRulePanel().getLogicRelation());

// 低权限规则内容Base64编码
StringBuilder lowPrivilegeRulesContent = new StringBuilder();
for (RulePanel.Rule rule : permissionTestPanel.getConfigPanel().getLowPrivilegeRulePanel().getRules()) {
    lowPrivilegeRulesContent.append(rule.getType()).append("|").append(rule.getContent()).append("\n");
}
String encodedLowPrivilegeRules = Base64.getEncoder().encodeToString(lowPrivilegeRulesContent.toString().getBytes());
lowPrivilegeRules.put("rules", encodedLowPrivilegeRules);
configCenter.put("lowPrivilegeRules", lowPrivilegeRules);

解析

  • 规则逻辑关系(And/Or)决定了多条规则的生效条件(如“同时满足”或“任一满足”),是安全测试中规则执行的核心逻辑,必须准确保存;
  • 规则内容按“类型|内容”格式拼接(如"URL|/admin/*"),每条规则占一行(\n分隔),便于解析;
  • 同样使用Base64编码规则内容,避免特殊字符(如|、换行符)破坏JSON结构。

未授权规则、拦截过滤器规则的采集逻辑与低权限规则一致,此处不再赘述。

(3)保存/恢复设置采集

// 保存/恢复设置
Map<String, Object> saveRestore = new HashMap<>();
saveRestore.put("fileType", fileTypeComboBox.getSelectedItem());
saveRestore.put("exportFilter", exportFilterComboBox.getSelectedItem());
saveRestore.put("removeDuplicates", removeDuplicatesCheckBox.isSelected());
config.put("saveRestore", saveRestore);

解析

  • 保存文件类型、导出过滤选项和“去除重复项”状态,确保恢复后“保存/恢复”标签页的自身配置与原状态一致。

步骤3:序列化与写入文件

// 使用Gson将配置写入文件
Gson gson = new GsonBuilder().setPrettyPrinting().create();
try (FileOutputStream fos = new FileOutputStream(fileToSave)) {
    fos.write(gson.toJson(config).getBytes());
}

解析

  • GsonBuilder().setPrettyPrinting()生成格式化的JSON,便于测试人员手动查看或编辑配置文件;
  • 使用try-with-resources语法自动关闭文件流,避免资源泄露。

2.3.2 恢复配置:restoreConfiguration方法

该方法的核心流程为:打开文件选择器→读取JSON文件→解析并反序列化→还原配置到UI组件。

步骤1:文件选择与读取

JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("选择配置文件");

int userSelection = fileChooser.showOpenDialog(parent);

if (userSelection == JFileChooser.APPROVE_OPTION) {
    File fileToOpen = fileChooser.getSelectedFile();
    
    // 使用Gson读取配置文件
    Gson gson = new Gson();
    Map<String, Object> config;
    try (FileReader reader = new FileReader(fileToOpen)) {
        config = gson.fromJson(reader, new TypeToken<Map<String, Object>>(){}.getType());
    }
    // ...后续逻辑
}

解析

  • 通过JFileChooser选择本地JSON文件,使用Gson将文件内容反序列化为Map<String, Object>,便于按键提取配置项。

步骤2:配置还原到UI组件

(1)临时请求头还原

// 恢复临时请求头
Object encodedHeadersObj = configCenter.get("tempHeaders");
if (encodedHeadersObj instanceof String encodedHeaders) {
    byte[] decodedBytes = Base64.getDecoder().decode(encodedHeaders);
    String tempHeaders = new String(decodedBytes);
    permissionTestPanel.getConfigPanel().setTempHeaderContent(tempHeaders);
}

解析

  • 读取Base64编码的请求头,解码后通过setTempHeaderContent方法设置到UI组件,还原原始请求头(含换行符)。

(2)复选框状态还原

// 恢复复选框状态
Object checkBoxesObj = configCenter.get("checkBoxes");
if (checkBoxesObj instanceof Map) {
    Map<String, Object> checkBoxes = convertMap(checkBoxesObj);
    
    Object checkUnauthorizedObj = checkBoxes.get("checkUnauthorized");
    if (checkUnauthorizedObj instanceof Boolean) {
        permissionTestPanel.getConfigPanel().setUnauthorizedCheck((Boolean) checkUnauthorizedObj);
    }
    // 其他复选框还原逻辑类似
}

解析

  • 通过convertMap方法将Object转为Map<String, Object>(避免类型转换异常),按键提取布尔值并设置到对应复选框。

(3)规则列表还原

// 恢复低权限规则
Object lowPrivilegeRulesObj = configCenter.get("lowPrivilegeRules");
if (lowPrivilegeRulesObj instanceof Map) {
    Map<String, Object> lowPrivilegeRules = convertMap(lowPrivilegeRulesObj);
    
    // 还原逻辑关系
    Object logicRelationObj = lowPrivilegeRules.get("logicRelation");
    if (logicRelationObj instanceof String) {
        permissionTestPanel.getConfigPanel().getLowPrivilegeRulePanel().setLogicRelation((String) logicRelationObj);
    }
    
    // 还原规则内容
    Object encodedRulesObj = lowPrivilegeRules.get("rules");
    if (encodedRulesObj instanceof String encodedRules) {
        byte[] decodedBytes = Base64.getDecoder().decode(encodedRules);
        String rulesContent = new String(decodedBytes);
        permissionTestPanel.getConfigPanel().getLowPrivilegeRulePanel().loadRules(rulesContent);
    }
}

解析

  • 逻辑关系通过setLogicRelation方法直接设置到下拉框;
  • 规则内容解码后调用loadRules方法解析(按\n分割行,按|分割类型与内容),并添加到规则表格中。

拦截过滤器规则的还原使用loadRulesForConfigRestore方法,与普通规则的区别是会清空默认规则,确保完全覆盖为配置文件中的内容。

(4)保存/恢复设置还原

// 恢复保存/恢复设置
Object saveRestoreObj = config.get("saveRestore");
if (saveRestoreObj instanceof Map) {
    Map<String, Object> saveRestore = convertMap(saveRestoreObj);
    
    Object fileTypeObj = saveRestore.get("fileType");
    if (fileTypeObj != null) {
        fileTypeComboBox.setSelectedItem(fileTypeObj.toString());
    }
    // 导出过滤和去除重复项还原逻辑类似
}

解析

  • 下拉框选项通过setSelectedItem设置(直接使用字符串匹配);
  • 复选框状态通过setSelected方法还原。

2.3.3 辅助方法:convertMap

private static Map<String, Object> convertMap(Object obj) {
    if (obj instanceof Map) {
        // 使用Gson将对象重新序列化和反序列化
        Gson gson = new Gson();
        String json = gson.toJson(obj);
        return gson.fromJson(json, new TypeToken<Map<String, Object>>(){}.getType());
    }
    return new HashMap<>();
}

解析

  • 由于Gson反序列化的Map可能包含嵌套的LinkedTreeMap(Gson内部类型),直接转换为Map<String, Object>可能出现类型异常;
  • 通过“序列化→反序列化”的二次处理,确保返回标准的Map<String, Object>,避免后续取值时的类型错误。

2.4 相关面板的方法补充

为支持配置的采集与还原,需在现有面板中添加getter/setter方法,暴露配置数据或提供设置入口。

2.4.1 PermissionTestPanel:暴露配置面板

/**
 * 获取配置面板
 * @return ConfigPanel实例
 */
public ConfigPanel getConfigPanel() {
    return configPanel;
}

解析PermissionTestPanel是主面板,通过getConfigPanel方法允许ConfigUtil访问内部的ConfigPanel(包含请求头、复选框等配置)。

2.4.2 RulePanel:规则操作方法

/**
 * 设置逻辑关系
 * @param logicRelation 逻辑关系 ("And" 或 "Or")
 */
public void setLogicRelation(String logicRelation) {
    logicComboBox.setSelectedItem(logicRelation);
}

/**
 * 从字符串加载规则
 * @param rulesContent 规则内容字符串
 */
public void loadRules(String rulesContent) {
    // 清空现有规则
    clearAllRules();

    // 解析规则内容
    String[] lines = rulesContent.split("\n");
    for (String line : lines) {
        if (!line.trim().isEmpty()) {
            String[] parts = line.split("\\|", 2);
            if (parts.length == 2) {
                Rule rule = new Rule(parts[0], parts[1]);
                rules.add(rule);
                Object[] rowData = {rule.getType(), rule.getContent()};
                tableModel.addRow(rowData);
            }
        }
    }
}

解析

  • setLogicRelation用于还原规则逻辑关系;
  • loadRules解析规则字符串(按\n|分割),清空现有规则后添加新规则到表格,实现规则列表的完全替换。

2.4.3 InterceptFilterPanel:特殊规则还原方法

/**
 * 从字符串加载规则(用于配置恢复,完全替换现有规则,包括默认规则)
 * @param rulesContent 规则内容字符串
 */
public void loadRulesForConfigRestore(String rulesContent) {
    // 清空现有规则(包括默认规则)
    rules.clear();
    tableModel.setRowCount(0);
    ruleContentArea.setText("");

    // 解析规则内容(同RulePanel的loadRules)
    String[] lines = rulesContent.split("\n");
    for (String line : lines) {
        if (!line.trim().isEmpty()) {
            String[] parts = line.split("\\|", 2);
            if (parts.length == 2) {
                Rule rule = new Rule(parts[0], parts[1]);
                rules.add(rule);
                Object[] rowData = {rule.getType(), rule.getContent()};
                tableModel.addRow(rowData);
            }
        }
    }
}

解析:拦截过滤器规则默认包含基础规则,恢复时需完全覆盖(而非追加),因此单独实现loadRulesForConfigRestore方法,确保清空默认规则后再加载配置文件中的内容。

2.4.4 ConfigPanel:复选框状态设置

// 添加设置复选框状态的方法
public void setUnauthorizedCheck(boolean selected) {
    unauthorizedCheck.setSelected(selected);
}

public void setIgnore204304Check(boolean selected) {
    ignore204304Check.setSelected(selected);
}

public void setAvoid304Check(boolean selected) {
    avoid304Check.setSelected(selected);
}

public void setUseConfigRulesCheck(boolean selected) {
    useConfigRulesCheck.setSelected(selected);
}

解析:为4个核心复选框添加setter方法,允许ConfigUtil直接设置其选中状态,还原配置时无需暴露组件本身,符合封装原则。

三、功能价值与安全测试场景

3.1 提升测试效率

安全测试中,同一类场景(如API未授权测试)往往需要重复使用相同的请求头、规则列表。通过“保存→恢复”功能,测试人员可避免重复配置,将时间集中在核心测试逻辑上。

3.2 保证环境一致性

多人协作时,不同测试人员的本地配置可能存在差异(如规则逻辑、过滤条件),导致测试结果不可复现。通过共享JSON配置文件,团队可使用统一的测试环境,确保结果的可比性。

3.3 适配复杂配置场景

  • 临时请求头:安全测试中常需添加自定义CookieAuthorization等头信息,Base64编码确保特殊字符(如;、空格)不丢失;
  • 规则列表:按“类型|内容”格式保存的规则,既便于机器解析,也便于测试人员手动编辑(如通过JSON文件批量修改规则)。

总结

配置保存与恢复功能通过UI扩展、数据编码与序列化技术,为安全测试插件提供了可靠的配置管理能力。其核心价值在于:简化重复操作、保证环境一致性、适配复杂测试场景。

从技术实现来看,ConfigUtil工具类通过Gson实现JSON转换,通过Base64处理特殊文本,结合面板方法的合理暴露,实现了配置的完整保存与还原。对于安全测试人员而言,无需关注底层实现细节,只需通过“保存”“恢复”按钮即可快速复用配置,显著提升工作效率。


本插件代码开源地址:Gitee代码仓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

介一笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值