我有一个表单,我有文本框和提交按钮。我正在使用burp工具更改表单中提交的值以测试我的服务器端验证。
服务器端验证工作正常,除非从burp工具输入%
字符,在输入%
字符时服务器显示以下异常。
2012-12-11 11:37:07,860 WARN [org.apache.tomcat.util.http.Parameters] (ajp-0.0.0.0-8109-19) Parameters: Character decoding failed. Parameter skipped.
java.io.CharConversionException: EOF
at org.apache.tomcat.util.buf.UDecoder.convert(UDecoder.java:83)
at org.apache.tomcat.util.buf.UDecoder.convert(UDecoder.java:49)
at org.apache.tomcat.util.http.Parameters.urlDecode(Parameters.java:429)
at org.apache.tomcat.util.http.Parameters.processParameters(Parameters.java:412)
at org.apache.tomcat.util.http.Parameters.processParameters(Parameters.java:363)
at org.apache.catalina.connector.Request.parseParameters(Request.java:2562)
at org.apache.catalina.connector.Request.getParameter(Request.java:1060)
at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:355)
at org.displaytag.filter.ResponseOverrideFilter.doFilter(ResponseOverrideFilter.java:118)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:436)
at org.apache.coyote.ajp.AjpProtocol$AjpConnectionHandler.process(AjpProtocol.java:384)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Thread.java:662)
当我提交带有%字符的表单时,它会正确地将其更改为%25,但如何处理此服务器端?
答案 0 :(得分:1)
您遇到的问题是解码器在以%
符号开头时需要一个有效的,可解码的参数。提交表单时,输入参数%
被正确编码为%25
,当您使用Burp编辑此请求时,此编码并不会发生。 Burp心甘情愿地向您的服务器发送%
符号,并且您的服务器端验证假设这是编码值的开头,但是无法解码,因为它基本上是一个破坏的参数。
我最好的猜测不是单独发送%-values(即没有数字值)。它就像在null引用上调用一个方法一样。有些东西只是不起作用。我会说你尝试检查百分比符号是否被你自己的输入验证批准或拒绝,但只要它首先通过解码,它就必须被编码。
答案 1 :(得分:0)
通过此测试(包括应该是URL编码数据的原始'%'),您正在测试您的应用是否正确拒绝格式错误的输入。没错。
如果您想使用burp推送数据,则需要对其进行编码,这意味着您需要将'%25'放在需要'%'的位置。
答案 2 :(得分:0)
如果仔细检查堆栈跟踪,所涉及的所有类(在将HTTP文本解析为Java请求对象时)都来自框架,并且没有用户对其进行控制。
现在背后的基本前提是:服务器将始终假设它收到的数据始终是URL编码,而HENCE总是会尝试解码。
如果解码失败,则意味着不遵守HTTP协议的基本原则,因此在不给予用户处理请求的情况下完全拒绝请求。
恕我直言,你无法处理这种情况,因为服务器本身正确处理它。
修改强>
@Ravi在之上提出的解决方案看起来就像解决问题一样。我尝试了相同的代码,但我永远无法捕获异常!
深入挖掘,很明显为什么在任何用户定义的代码中都无法处理异常(java.io.CharConversionException
)。
虽然抛出java.io.CharConversionException
但它从未在调用层次结构中传播。这就是原因,
在堆栈跟踪中
at org.apache.tomcat.util.buf.UDecoder.convert(UDecoder.java:83)
at org.apache.tomcat.util.buf.UDecoder.convert(UDecoder.java:49)
at org.apache.tomcat.util.http.Parameters.urlDecode(Parameters.java:429)
at org.apache.tomcat.util.http.Parameters.processParameters(Parameters.java:412)
查看课程processParameters()
中的方法org.apache.tomcat.util.http.Parameters
的来源(代码取自grepcode)
public void processParameters( byte bytes[], int start, int len,
String enc ) {
int end=start+len;
int pos=start;
if( debug>0 )
log( "Bytes: " + new String( bytes, start, len ));
do {
boolean noEq=false;
int valStart=-1;
int valEnd=-1;
int nameStart=pos;
int nameEnd=ByteChunk.indexOf(bytes, nameStart, end, '=' );
// Workaround for a&b&c encoding
int nameEnd2=ByteChunk.indexOf(bytes, nameStart, end, '&' );
if( (nameEnd2!=-1 ) &&
( nameEnd==-1 || nameEnd > nameEnd2) ) {
nameEnd=nameEnd2;
noEq=true;
valStart=nameEnd;
valEnd=nameEnd;
if( debug>0) log("no equal " + nameStart + " " + nameEnd + " " + new String(bytes, nameStart, nameEnd-nameStart) );
}
if( nameEnd== -1 )
nameEnd=end;
if( ! noEq ) {
valStart= (nameEnd < end) ? nameEnd+1 : end;
valEnd=ByteChunk.indexOf(bytes, valStart, end, '&');
if( valEnd== -1 ) valEnd = (valStart < end) ? end : valStart;
}
pos=valEnd+1;
if( nameEnd<=nameStart ) {
log.warn("Parameters: Invalid chunk ignored.");
continue;
// invalid chunk - it's better to ignore
}
tmpName.setBytes( bytes, nameStart, nameEnd-nameStart );
tmpValue.setBytes( bytes, valStart, valEnd-valStart );
try {
addParam( urlDecode(tmpName, enc), urlDecode(tmpValue, enc) );
} catch (IOException e) {
// Exception during character decoding: skip parameter
log.warn("Parameters: Character decoding failed. " +
"Parameter skipped.", e);
}
tmpName.recycle();
tmpValue.recycle();
} while( pos<end );
}
在该方法中,有do-while循环用于解析所有请求参数。查看方法循环结束处的 try-catch块。有评论说
// Exception during character decoding: skip parameter
所以即使抛出异常但未传播。它作为警告消息记录并且参数值设置为null。
所以很明显,你永远无法捕捉到那个异常(java.io.CharConversionException
)
答案 3 :(得分:0)
其他答案是正确的,因为考虑到您可以安全地要求您对输入进行编码,因为所有浏览器都会这样做,您理想情况下永远不会收到未编码的普通%
字符。
但是,如果你说,如果你仍然坚持看错页面;可以编写自定义过滤器来捕获此CharConversionException
并向客户端发送错误页面或将请求转发到相同的URI但设置了errorMessage
属性,以便以不同方式处理请求。
此处通过名为useErrorPage
的过滤器参数进行配置。
编辑:根据@Santosh共享的源代码,显示Tomcat实际上正在抑制CharConversionException
我修改过滤器以注入参数验证并检查所有请求可以通过名为forbiddenChars
的过滤器参数配置的禁用字符列表的参数。
将此过滤器放在web.xml中的DisplayTag的ResponseOverrideFilter上方,以确保它拦截所有内容。在过滤器方面,订购可能会产生副作用。
我会把这次编辑视为一个机会,强调 Servlet过滤器是一个非常强大的概念,可让您以任何您喜欢的方式预先或后期处理任何请求。大多数Web框架(如Struts 2 for MVC)都使用过滤器作为入口点。
因此,您无法使用过滤器。只是确保你将它们用于正确的目的,就像业务逻辑显然不应该去那里,尽管应用程序范围的身份验证可以。
IOException过滤器
public class CharConversionExpFilter implements Filter {
private char[] forbiddenChars; // ADDED
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
forbiddenChars = filterConfig.getInitParameter("forbiddenChars")
.replace(",", "").toCharArray(); // ADDED
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String requestURI = ((HttpServletRequest) request).getRequestURI();
try {
validateParameters((HttpServletRequest) request); // ADDED
chain.doFilter(request, response);
} catch (IOException e) {
if (e instanceof CharConversionException) {
if ("true".equalsIgnoreCase(filterConfig.getInitParameter("useErrorPage"))) {
if (response instanceof HttpServletResponse) {
((HttpServletResponse) response).sendError(400,
"The request cannot be fulfilled due to bad input.\nError:" + e.getMessage());
}
} else {
request.setAttribute("errorMessage", e.getMessage());
filterConfig.getServletContext().getRequestDispatcher(requestURI).forward(request, response);
}
}
}
}
// ADDED
private void validateParameters(HttpServletRequest request) throws CharConversionException {
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameter = request.getParameter(parameterNames.nextElement());
if (parameter != null && parameter.length() > 0) {
for (char forbidChar : forbiddenChars) {
if (parameter.indexOf(forbidChar) != -1) {
throw new CharConversionException(
String.format(
"Parameter: [%s] contains the forbidden character [%c]",
parameter, forbidChar));
}
}
}
}
}
@Override
public void destroy() {}
}
的的web.xml 强>
<filter>
<filter-name>CharConversionExpFilter</filter-name>
<filter-class>servlet.filter.CharConversionExpFilter</filter-class>
<init-param>
<param-name>useErrorPage</param-name>
<param-value>true</param-value>
</init-param>
<init-param> <!-- ADDED -->
<param-name>forbiddenChars</param-name>
<param-value>%,*,?,#,$</param-value> <!-- filtering wildcards and EL chars -->
</init-param>
</filter>
<filter-mapping>
<filter-name>CharConversionExpFilter</filter-name>
<url-pattern>/*</url-pattern> <!-- * = ALL; "/servlet-name" if required -->
</filter-mapping>
<error-page>
<error-code>400</error-code>
<location>/errorPage.jsp</location> <!-- isErrorPage = true; use "exception" obj -->
</error-page>
希望这能为您提供足够的指示,以根据您的需求制定解决方案。 (注意:语法突出显示错误地将<url-pattern>/*
解释为多行Java注释的开头。请忽略它。)