4

我的一个 REST API 需要一个属性“url”,它需要一个 URL 作为用户的输入。我正在使用 ESAPI 来防止 XSS 攻击。问题是用户提供的 URL 类似于

http://example.com/alpha?abc=def&phil=key%3dbdj

来自 ESAPI 编码器的 cannonicalize 方法在此处抛出入侵异常,声称输入具有混合编码,因为它是 url 编码的,并且片段 '&phi' 被视为 HTML 编码,因此是异常。

我在清理我的一个应用程序 url 时遇到了类似的问题,其中第二个查询参数以“pa”或“pi”开头,并通过 HTML 解码转换为 delta 或 pi 字符。请在此处参考我之前的 Stackoverflow 问题

现在问题是,由于整个 URL 是作为用户输入的,我不能简单地解析查询参数并单独清理它们,因为可以创建恶意输入并结合两个查询参数并单独清理它们不会起作用案子。

示例:<scr 是第一个查询参数值和 ipt>alert(0) 的最后一部分;或者某些东西作为下一个查询参数控制上下文的第一部分。

有没有人遇到过类似的问题?我真的很想知道你们实施了哪些解决方案。感谢您的任何指示。

编辑:来自“avgvstvs”的以下答案不会引发入侵异常(谢谢!!)。但是,cannonicalize 方法现在更改了原始输入字符串。ESAPI 将查询参数的 &phi 视为一些 html 编码的字符并将其替换为 '?' 字符。类似于我之前的问题,此处链接。不同之处在于这是我的应用程序的 URL,而这是用户输入。在这里维护白名单是我唯一的选择吗?

4

1 回答 1

2

您在这里面临的问题是,对 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 的可能性,如果需要,请相应地修改此代码。)

于 2014-05-03T18:23:32.907 回答