代码审计系列之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 主要安全问题
- debug模式开启:生产环境应关闭
- 默认路径可猜测:常见路径为
/dwr/和/exec/ - JSON-RPC和JSONP默认开启:可能绕过安全保护
- 未授权访问风险:第三方调用接口通常未授权
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 审计流程
-
识别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/
- 关闭debug模式:
-
访问控制:
- 为DWR接口添加认证
- 实现细粒度的权限控制
-
输入验证:
- 对所有输入参数进行严格验证
- 限制可转换的对象类型
-
日志记录:
- 记录所有DWR调用
- 监控异常调用模式
5. 总结
DWR框架为AJAX开发提供了便利,但也带来了诸多安全隐患。开发人员应:
- 了解DWR的工作原理和调用机制
- 熟悉各种参数类型的调用方式
- 严格遵循安全配置指南
- 实施全面的安全审计
安全审计人员应关注:
- 默认配置带来的风险
- 未授权访问问题
- 复杂参数调用的安全隐患
- 文件上传和反序列化等高风险操作
通过合理配置和安全编码实践,可以在享受DWR便利性的同时确保系统安全。