我的一个REST API期待一个属性" url"期望URL作为用户的输入。我正在使用ESAPI来防止XSS攻击。问题是用户提供的URL类似于
http://example.com/alpha?abc=def&phil=key%3dbdj
来自ESAPI编码器的cannonicalize方法抛出入侵异常,声称输入具有混合编码,因为它是url编码的并且片段是'& phi'被视为HTML编码,因此是例外。
我在清理其中一个应用程序网址时出现了类似的问题,其中第二个查询参数以' pa'或者' pi'并通过HTML解码转换为delta或pi字符。请参阅我的previous Stackoverflow question here
现在问题在于,由于整个URL都是来自用户的输入,我不能简单地解析查询参数并单独清理它们,因为可以结合两个查询参数创建恶意输入并单独清理它们不会在那种情况下工作。
示例:& ltscr来自第一个查询参数值和ipt& gtalert(0)的最后一部分;或者某些东西作为下一个查询参数控制上下文的第一部分。
有没有人遇到过类似的问题?我真的很想知道你们实施了哪些解决方案。谢谢你的任何指示。
编辑:#av;不会抛出入侵异常(谢谢!)。但是,cannonicalize方法现在更改原始输入字符串。 ESAPI将查询参数的& phi视为一些html编码的char并将其替换为'?'焦炭。像我之前在这里链接的问题。区别在于我的应用程序的URL,而这是用户输入。我唯一的选择是在这里保留一份白名单吗?答案 0 :(得分:2)
您在这里遇到的问题是,对URL的不同部分进行编码有不同的规则 - 对于内存,URL中有4个具有不同编码规则的部分。首先,了解为什么在Java中,您需要使用UriBuilder
类来构建URL。网址specification将有助于细节的细节。
现在问题是因为整个网址都是输入 从用户,我不能简单地解析出Query参数和 因为可以创建恶意输入,所以可以单独清理它们 组合两个查询参数并单独清理它们 在这种情况下不会工作。
这里唯一真正的选择是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 reference还有其他方法会提取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格式。网址很可能被视为特殊情况,因此很可能会故意将其排除在考虑范围之外。这是我处理你案件的方式 - 没有白名单,它将保证你受到Encoder.canonicalize()
的保护。
使用上面的代码获取输入的URI表示。
第1步:规范化URI.getQuery()
以外的所有URI部分
步骤2:使用库解析器将查询字符串解析为数据结构。我会使用来自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(从而减轻了我们的所有负担),然后在我们解析每个部分之后对这些部分进行规范化。
请注意,我列出的代码不适用于所有网址类型...网址的部分多于方案/权限/路径/查询。 (缺少userInfo或端口的可能性,如果需要,请相应地修改此代码。)