您在这里面临的问题是,对 URL 的不同部分进行编码有不同的规则——在内存中,URL 中有 4 个部分具有不同的编码规则。首先,了解为什么在 Java 中需要使用UriBuilder
类来构建 URL。URL规范将有助于处理细节。
现在问题是,由于整个 URL 是作为用户输入的,我不能简单地解析查询参数并单独清理它们,因为可以创建恶意输入并结合两个查询参数并单独清理它们不会起作用案子。
这里唯一真正的选择是java.net.URI
.
尝试这个:
URI dirtyURI = new URI("http://example.com/alpha?abc=def&phil=key%3dbdj");
String cleanURIStr = enc.canonicalize( dirtyURI.getPath() );
调用URI.getPath()
应该给你一个非百分比编码的 URL,如果enc.canonicalize()
在那个阶段之后检测到双重编码,那么你确实有一个双重编码的字符串,并且应该通知调用者你将只接受单编码的 URL 字符串。它URI.getPath()
足够聪明,可以对 URL 字符串的每个部分使用解码规则。
如果它仍然给您带来一些麻烦,API 参考有其他方法可以提取 URL 的其他部分,以防您需要对 URL 的不同部分执行不同的操作。例如,如果您需要手动解析 GET 请求的参数,您实际上可以让它返回查询字符串本身——它会对其进行解码。
=============JUNIT 测试用例============
package org.owasp.esapi;
import java.net.URI;
import java.net.URISyntaxException;
import org.junit.Test;
public class TestURLValidation {
@Test
public void test() throws URISyntaxException {
Encoder enc = ESAPI.encoder();
String input = "http://example.com/alpha?abc=def&phil=key%3dbdj";
URI dirtyURI = new URI(input);
enc.canonicalize(dirtyURI.getQuery());
}
}
=================更新问题的答案======================
没有办法绕过它: Encoder.canonicalize()
旨在将转义字符序列减少为简化的原生 Java 形式。URL 很可能被认为是一种特殊情况,因此它们很可能被故意排除在考虑之外。这是我处理您案件的方式——没有白名单,它将保证您受到Encoder.canonicalize()
.
使用上面的代码获取输入的 URI 表示。
第 1 步:规范化除URI.getQuery()
第 2 步之外的所有 URI 部分:使用库解析器将查询字符串解析为数据结构。我会使用来自 commons 的 httpclient-4.3.3.jar 和 httpcore-4.3.3.jar。然后你会做这样的事情:
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.List;
import javax.ws.rs.core.UriBuilder;
import org.apache.http.client.utils.URLEncodedUtils;
import org.junit.Test;
import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Encoder;
public class TestURLValidation
{
@Test
public void test() throws URISyntaxException {
Encoder enc = ESAPI.encoder();
String input = "http://example.com/alpha?abc=def&phil=key%3dbdj";
URI dirtyURI = new URI(input);
UriBuilder uriData = UriBuilder.fromUri(enc.canonicalize(dirtyURI.getScheme()));
uriData.path(enc.canonicalize(enc.canonicalize(dirtyURI.getAuthority() + dirtyURI.getPath())));
println(uriData.build().toString());
List<org.apache.http.NameValuePair> params = URLEncodedUtils.parse(dirtyURI, "UTF-8");
Iterator<org.apache.http.NameValuePair> it = params.iterator();
while(it.hasNext()) {
org.apache.http.NameValuePair nValuePair = it.next();
uriData.queryParam(enc.canonicalize(nValuePair.getName()), enc.canonicalize(nValuePair.getValue()));
}
String canonicalizedUrl = uriData.build().toString();
println(canonicalizedUrl);
}
public static void println(String s) {
System.out.println(s);
}
}
我们在这里真正做的是使用标准库来解析 inputURL(从而减轻我们的所有负担),然后在我们解析完每个部分之后对部分进行规范化。
请注意,我列出的代码不适用于所有url 类型...... URL 的部分比方案/权限/路径/查询更多。(缺少 userInfo 或 port 的可能性,如果需要,请相应地修改此代码。)