我的一个REST api需要一个属性"url",它需要一个url作为用户的输入。我使用ESAPI来防止XSS攻击。问题是用户提供的URL类似于
http://example.com/alpha?abc=def&phil=key%3dbdj
来自ESAPI编码器的cannonicalize方法在这里抛出入侵异常,声称输入具有混合编码,因为它是url编码的,而片段'φ被视为HTML编码,因此异常。
我在处理我的一个应用程序url时遇到了类似的问题,其中第二个查询参数以'pa'或'pi'开头,并通过HTML解码转换为delta或pi字符。请参考我之前的Stackoverflow问题在这里
现在的问题是,因为整个URL都是来自用户的输入,我不能简单地解析出查询参数并单独对它们进行消毒,因为恶意输入可以结合两个查询参数创建,单独对它们进行消毒在这种情况下是行不通的。
示例:<scr是第一个查询参数值的最后一部分,ipt>alert(0);或者作为下一个查询参数控制上下文的第一部分出现。
有人遇到过类似的问题吗?我真的很想知道你们实施了什么解决方案。谢谢你的指点。
编辑:下面的回答从'avgvstvs'不会抛出入侵异常(谢谢!!)。然而,cannonicalize方法现在改变了原始输入字符串。ESAPI将查询参数的&phi视为一些html编码的字符,并将其替换为'?的字符。就像我之前的问题,链接在这里。区别在于那是我的应用的URL而这是用户输入。我唯一的选择是保留白名单吗?
你在这里面临的问题是,对URL的不同部分有不同的编码规则——记住,URL中有4个部分有不同的编码规则。首先,理解为什么在Java中需要使用UriBuilder
类构建url。URL规范将有助于提供基本的细节。
现在的问题是,因为整个URL作为输入,我不能简单地解析出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参考有其他方法可以提取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()
以外的所有URI部分步骤2:使用库解析器将查询字符串解析为数据结构。我将使用httpclient-4.3.3.jar和httpcore-4.3.3.jar从commons。然后,您将执行如下操作:
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的可能性,如果需要,请相应地修改此代码。)