代码审计系列之DWR开发框架
字数 1561 2025-08-05 08:35:55

DWR框架安全审计与开发指南

1. DWR框架简介

DWR(Direct Web Remoting)是一个远程web调用框架,它使得Ajax开发变得简单。利用DWR可以在客户端使用JavaScript直接调用服务器端的Java方法,并返回值给JavaScript,就像直接在本地客户端调用一样。

关键特性:

  • 动态生成JavaScript代码:DWR根据Java类来动态生成JavaScript代码
  • 简化AJAX开发:隐藏了XMLHttpRequest的复杂性
  • 支持多种数据类型:包括基本类型、数组、对象和文件等

参考链接:http://directwebremoting.org/dwr/index.html

2. DWR默认配置与安全问题

2.1 默认web.xml配置分析

<web-app id="dwr">
  <servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    <init-param>
      <param-name>debug</param-name>
      <param-value>true</param-value> <!-- 安全隐患 -->
    </init-param>
    <init-param>
      <param-name>jsonRpcEnabled</param-name>
      <param-value>true</param-value> <!-- 安全隐患 -->
    </init-param>
    <init-param>
      <param-name>jsonpEnabled</param-name>
      <param-value>true</param-value> <!-- 安全隐患 -->
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-pattern> <!-- 默认路径 -->
  </servlet-mapping>
</web-app>

2.2 主要安全问题

  1. debug模式开启:生产环境应关闭
  2. 默认路径可猜测:常见路径为/dwr//exec/
  3. JSON-RPC和JSONP默认开启:可能绕过安全保护
  4. 未授权访问风险:第三方调用接口通常未授权

3. DWR开发与调用示例

3.1 基本类型参数调用

Java类示例

package com.example.dwr.commontest;

public class CommonParams {
    public static String stringTest(String data) {
        return data;
    }
    
    public static int inTest(int data) {
        return data;
    }
}

dwr.xml配置

<create javascript="commonparams" creator="new">
    <param name="class" value="com.example.dwr.commontest.CommonParams" />
</create>

JavaScript调用

function stringTest(){
    commonparams.stringTest("abcd");
}

function integerTest(){
    commonparams.inTest(1234);
}

HTTP请求示例

POST /dwr/call/plaincall/commonparams.inTest.dwr HTTP/1.1
Content-Type: text/plain

callCount=1
nextReverseAjaxIndex=0
c0-scriptName=commonparams
c0-methodName=inTest
c0-id=0
c0-param0=int:1234
batchId=0
instanceId=0
page=%2F
scriptSessionId=J2YAzcntFgQYepoW~g!fuZdxeAR6Qy4ho9m/JZRRo9m-dCmbaYdn5

3.2 数组类型参数调用

Java类示例

package com.example.dwr.arraytest;

import org.apache.commons.lang.StringUtils;

public class ArrayParams {
    public String iniArrayTest(int[] data) {
        String template = "";
        String tmp = "";
        for(int i=0;i<data.length;i++)
            tmp = tmp+String.valueOf(data[i])+",";
        template = template+"int array as:["+ tmp+"]";
        return template;
    }

    public String strArrayTest(String[] data) {
        String template = "";
        String joinStr = StringUtils.join(data, ",");
        template = template+"string array as:["+ joinStr+"]";
        return template;
    }
}

dwr.xml配置

<create javascript="arrayparams" creator="new">
    <param name="class" value="com.example.dwr.arraytest.ArrayParams" />
</create>

JavaScript调用

function iniArrayTest(){
    var a=[1,2,3,4];
    arrayparams.iniArrayTest(a);
}

function strArrayTest(){
    var a = ['a','b','c','d']
    arrayparams.strArrayTest(a);
}

HTTP请求示例

POST /dwr/call/plaincall/arrayparams.strArrayTest.dwr HTTP/1.1
Content-Type: text/plain

callCount=1
nextReverseAjaxIndex=0
c0-scriptName=arrayparams
c0-methodName=strArrayTest
c0-id=0
c0-e1=string:a
c0-e2=string:b
c0-e3=string:c
c0-e4=string:d
c0-param0=array:[reference:c0-e1,reference:c0-e2,reference:c0-e3,reference:c0-e4]
batchId=1
instanceId=0
page=%2Farrtest.jsp
scriptSessionId=J2YAzcntFgQYepoW~g!fuZdxeAR6Qy4ho9m/WNnUo9m-uxKcm4x0i

3.3 对象类型参数调用

Java类示例

package com.example.dwr.objecttest;

public class ObjectTest {
    public String addUser(UserBean user){
        return "Name:"+user.getName();
    }
}

public class UserBean {
    private String name;
    // getter和setter方法
}

dwr.xml配置

<create javascript="objecttest" creator="new">
    <param name="class" value="com.example.dwr.objecttest.ObjectTest"/>
</create>
<convert match="com.example.dwr.objecttest.UserBean" converter="bean"/>

JavaScript调用

function objectTest(){
    var user={name:"jkgh006"};
    objecttest.addUser(user);
}

HTTP请求示例

POST /dwr/call/plaincall/objecttest.addUser.dwr HTTP/1.1
Content-Type: text/plain

callCount=1
nextReverseAjaxIndex=0
c0-scriptName=objecttest
c0-methodName=addUser
c0-id=0
c0-e1=string:jkgh006
c0-param0=Object_Object:{name:reference:c0-e1}
batchId=1
instanceId=0
page=%2Fobjtest.jsp
scriptSessionId=J2YAzcntFgQYepoW~g!fuZdxeAR6Qy4ho9m/Y*v$o9m-3BE1kSgDb

3.4 文件上传调用

Java类示例

package com.example.dwr.filetest;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;

public class FileTest {
    public String upload(InputStream inputStream, String fileName) throws IOException {
        String tempFileName= FilenameUtils.getName(fileName);
        String path=getRealPath("upload");
        File file=new File(path+ File.separator+tempFileName);
        FileUtils.copyInputStreamToFile(inputStream, file);
        return file.getPath();
    }

    public String getRealPath(String dir){
        WebContext context= WebContextFactory.get();
        return context.getSession().getServletContext().getRealPath(dir);
    }
}

dwr.xml配置

<create javascript="filtest" creator="new">
    <param name="class" value="com.example.dwr.filetest.FileTest" />
</create>

JavaScript调用

function upload(){
    var file=dwr.util.getValue("file");
    filtest.upload(file,file.value,function(result){
        alert(result)
    });
}

HTTP请求示例

POST /dwr/call/htmlcall/filtest.upload.dwr HTTP/1.1
Content-Type: multipart/form-data; boundary=252533190231463

--252533190231463
Content-Disposition: form-data; name="callCount"
1
--252533190231463
Content-Disposition: form-data; name="c0-scriptName"
filtest
--252533190231463
Content-Disposition: form-data; name="c0-methodName"
upload
--252533190231463
Content-Disposition: form-data; name="c0-param0"; filename="up.gif"
Content-Type: image/gif
[文件内容]
--252533190231463
Content-Disposition: form-data; name="c0-param1"
string:C%3A%5Cfakepath%5Cup.gif
--252533190231463--

3.5 综合类型参数调用

Java类示例

package com.example.dwr.complextest;

import com.example.dwr.objecttest.UserBean;
import org.apache.commons.lang.StringUtils;

public class ComplexParams {
    public String intAndStringAndArrayAndObjTest(int a, String b, int[] as, 
            String[] bs, UserBean user, UserBean[] users) {
        // 实现逻辑
    }
}

dwr.xml配置

<create javascript="complexparams" creator="new">
    <param name="class" value="com.example.dwr.complextest.ComplexParams" />
</create>

JavaScript调用

function complexTest(){
    var a = 1234;
    var b = 'abcd';
    var c = [1,2,3,4];
    var d = ['a','b','c','d'];
    var e ={name:"jkgh006"};
    var f = [{name:"jkgh006"},{name:"jkgh007"},{name:"jkgh008"}]
    complexparams.intAndStringAndArrayAndObjTest(a,b,c,d,e,f);
}

HTTP请求示例

POST /dwr/call/plaincall/complexparams.intAndStringAndArrayAndObjTest.dwr HTTP/1.1
Content-Type: text/plain

callCount=1
nextReverseAjaxIndex=0
c0-scriptName=complexparams
c0-methodName=intAndStringAndArrayAndObjTest
c0-id=0
c0-param0=number:1234
c0-param1=string:abcd
c0-e1=number:1
c0-e2=number:2
c0-e3=number:3
c0-e4=number:4
c0-param2=array:[reference:c0-e1,reference:c0-e2,reference:c0-e3,reference:c0-e4]
c0-e5=string:a
c0-e6=string:b
c0-e7=string:c
c0-e8=string:d
c0-param3=array:[reference:c0-e5,reference:c0-e6,reference:c0-e7,reference:c0-e8]
c0-e9=string:jkgh006
c0-param4=Object_Object:{name:reference:c0-e9}
c0-e11=string:jkgh006
c0-e10=Object_Object:{name:reference:c0-e11}
c0-e13=string:jkgh007
c0-e12=Object_Object:{name:reference:c0-e13}
c0-e15=string:jkgh008
c0-e14=Object_Object:{name:reference:c0-e15}
c0-param5=array:[reference:c0-e10,reference:c0-e12,reference:c0-e14]
batchId=0
instanceId=0
page=%2Fcomplextest.jsp
scriptSessionId=J2YAzcntFgQYepoW~g!fuZdxeAR6Qy4ho9m/uwpcp9m-VQn1SkeIe

4. DWR安全审计要点

4.1 审计流程

  1. 识别DWR接口

    • 尝试访问/dwr//exec/路径
    • 检查web.xml中DWR配置
  2. 接口枚举

    • 访问/dwr/查看所有可用接口(debug模式开启时)
    • 检查/dwr/engine.js/dwr/util.js是否存在
  3. 构造请求

    • 根据接口文档或前端代码构造调用
    • 使用上述示例中的请求模板
  4. 权限验证

    • 测试未授权访问
    • 测试越权访问

4.2 常见漏洞

  1. 未授权访问:DWR接口通常缺乏认证
  2. 反序列化漏洞:不当的对象转换可能导致RCE
  3. 文件上传漏洞:未校验文件类型和内容
  4. 信息泄露:debug接口暴露系统信息
  5. CSRF漏洞:缺乏CSRF防护

4.3 安全建议

  1. 生产环境配置

    • 关闭debug模式:<param-name>debug</param-name><param-value>false</param-value>
    • 禁用JSON-RPC和JSONP:<param-name>jsonRpcEnabled</param-name><param-value>false</param-value>
    • 修改默认路径:避免使用/dwr//exec/
  2. 访问控制

    • 为DWR接口添加认证
    • 实现细粒度的权限控制
  3. 输入验证

    • 对所有输入参数进行严格验证
    • 限制可转换的对象类型
  4. 日志记录

    • 记录所有DWR调用
    • 监控异常调用模式

5. 总结

DWR框架为AJAX开发提供了便利,但也带来了诸多安全隐患。开发人员应:

  1. 了解DWR的工作原理和调用机制
  2. 熟悉各种参数类型的调用方式
  3. 严格遵循安全配置指南
  4. 实施全面的安全审计

安全审计人员应关注:

  1. 默认配置带来的风险
  2. 未授权访问问题
  3. 复杂参数调用的安全隐患
  4. 文件上传和反序列化等高风险操作

通过合理配置和安全编码实践,可以在享受DWR便利性的同时确保系统安全。

DWR框架安全审计与开发指南 1. DWR框架简介 DWR(Direct Web Remoting)是一个远程web调用框架,它使得Ajax开发变得简单。利用DWR可以在客户端使用JavaScript直接调用服务器端的Java方法,并返回值给JavaScript,就像直接在本地客户端调用一样。 关键特性: 动态生成JavaScript代码:DWR根据Java类来动态生成JavaScript代码 简化AJAX开发:隐藏了XMLHttpRequest的复杂性 支持多种数据类型:包括基本类型、数组、对象和文件等 参考链接:http://directwebremoting.org/dwr/index.html 2. DWR默认配置与安全问题 2.1 默认web.xml配置分析 2.2 主要安全问题 debug模式开启 :生产环境应关闭 默认路径可猜测 :常见路径为 /dwr/ 和 /exec/ JSON-RPC和JSONP默认开启 :可能绕过安全保护 未授权访问风险 :第三方调用接口通常未授权 3. DWR开发与调用示例 3.1 基本类型参数调用 Java类示例 : dwr.xml配置 : JavaScript调用 : HTTP请求示例 : 3.2 数组类型参数调用 Java类示例 : dwr.xml配置 : JavaScript调用 : HTTP请求示例 : 3.3 对象类型参数调用 Java类示例 : dwr.xml配置 : JavaScript调用 : HTTP请求示例 : 3.4 文件上传调用 Java类示例 : dwr.xml配置 : JavaScript调用 : HTTP请求示例 : 3.5 综合类型参数调用 Java类示例 : dwr.xml配置 : JavaScript调用 : HTTP请求示例 : 4. DWR安全审计要点 4.1 审计流程 识别DWR接口 : 尝试访问 /dwr/ 和 /exec/ 路径 检查web.xml中DWR配置 接口枚举 : 访问 /dwr/ 查看所有可用接口(debug模式开启时) 检查 /dwr/engine.js 和 /dwr/util.js 是否存在 构造请求 : 根据接口文档或前端代码构造调用 使用上述示例中的请求模板 权限验证 : 测试未授权访问 测试越权访问 4.2 常见漏洞 未授权访问 :DWR接口通常缺乏认证 反序列化漏洞 :不当的对象转换可能导致RCE 文件上传漏洞 :未校验文件类型和内容 信息泄露 :debug接口暴露系统信息 CSRF漏洞 :缺乏CSRF防护 4.3 安全建议 生产环境配置 : 关闭debug模式: <param-name>debug</param-name><param-value>false</param-value> 禁用JSON-RPC和JSONP: <param-name>jsonRpcEnabled</param-name><param-value>false</param-value> 修改默认路径:避免使用 /dwr/ 和 /exec/ 访问控制 : 为DWR接口添加认证 实现细粒度的权限控制 输入验证 : 对所有输入参数进行严格验证 限制可转换的对象类型 日志记录 : 记录所有DWR调用 监控异常调用模式 5. 总结 DWR框架为AJAX开发提供了便利,但也带来了诸多安全隐患。开发人员应: 了解DWR的工作原理和调用机制 熟悉各种参数类型的调用方式 严格遵循安全配置指南 实施全面的安全审计 安全审计人员应关注: 默认配置带来的风险 未授权访问问题 复杂参数调用的安全隐患 文件上传和反序列化等高风险操作 通过合理配置和安全编码实践,可以在享受DWR便利性的同时确保系统安全。