美文网首页Tomcat
IE浏览器中文参数导致400 Bad Request问题研究

IE浏览器中文参数导致400 Bad Request问题研究

作者: IamShuhao | 来源:发表于2018-02-06 11:21 被阅读122次

    问题描述

    本人在使用Jboss-eap-7.1(内核是Wildfly)时,向服务器发送带中文url参数的Get请求时,服务器会返回400错误。而且应用根本接收不到该请求的信息,在连接层面就被拦截了,没有将请求转给应用处理。不止是Wildfly,tomcat的7,8版本也存在一样的情况。

    问题探究

    经过实验,发现IE浏览器6, 8, 11版本均存在该问题,但chrome浏览器正常,微软的Edge浏览器也是正常的。除了中文,如果参数含有'<', '{'等特殊字符时,IE浏览器也会报400错误。经过查找资料和抓包分析,发现是编码问题导致了两个浏览器的结果不同,但不是大家一般认为的字符集的原因。下面是不同浏览器针对GET请求URL编码情况的分析。对于GET URL http://example.com/test?lover=樊

    • chrome浏览器先使用UTF-8字符集编码URL中的所有字符,再使用 %编码(Percent-Encoding)将字节转换为%HH(%+两个十六进制数字)的形式,这样可以保证URL中全部是标准ASCII字符。樊的UTF-8编码为{E6, A8, 8A},实际发送的请求是 http://example.com/test?lover=%E6%A8%8A
    • IE浏览器会使用GBK字符集编码URL中的参数,但不会转换为%编码。樊的GBK编码为{B7, AE} 。实际发送的请求时,直接将樊字对应的GBK字节作为请求的内容。

    以上分析也许不太容易理解,我们再从字节的角度详细解释一下。
    下面是chrome浏览器发送请求的抓包内容,尾部的参数是=%E6%A8%8A,实际发送的字节为{ 3D, 25, 45, 36, 25, 41, 38, 25, 38, 41, 38, 41 }。'=' 的ASCII码为3D,'%' 的ASCII码为25,'E' 的ASCII码为45,以此类推。

    chrome请求抓包内容

    下面是IE浏览器发送请求的抓包内容,尾部的参数是=樊,实际发送的字节为{ 3D, B7, AE }。B7和AE都是超出RFC 3986规范允许的字节。

    IE请求抓包内容
    %编码是使用字符集编码后,再将每个超出规范允许范围的字节转换为 %+字节对应的十六进制 的形式。
    另外,IE中有一个选项,“发送UTF-8 URL”。选中之后,参数还是以GBK格式发送。这个仅仅对URL路径中的字符生效,选中就是UTF-8,不勾选就是GBK。但对于URL参数没有作用,永远都使用GBK字符集。IE会对路径中的字符做%编码,URL参数不做。

    根本原因

    所以问题的根本原因是IE没有遵循RFC 3986规范,未对URL参数使用 %编码,而是直接将字符对应的字节码发送到服务器。Wildfly服务器端的安全机制对此类URL做了拦截。它在该版本做了更新,遵循RFC 3986规范,对于url中含有非法字符的请求,直接予以拒绝,并返回400错误。相关代码摘录如下:

    private static final boolean[] ALLOWED_TARGET_CHARACTER = new boolean[256];
    ………… ………… ………… 
    for(int i = 0; i < 256; ++i) {
        if(i < 32 || i > 126) {
            ALLOWED_TARGET_CHARACTER[i] = false;
        } else {
            switch ((char)i) {
                case '\"':
                case '#':
                case '<':
                case '>':
                case '\\':
                case '^':
                case '`':
                case '{':
                case '|':
                case '}':
                    ALLOWED_TARGET_CHARACTER[i] = false;
                    break;
                default:
                    ALLOWED_TARGET_CHARACTER[i] = true;
            }
        }
    }
    ………… ………… ………… 
    while (buffer.hasRemaining()) {
        char next = (char) (buffer.get() & 0xFF);
        if(!allowUnescapedCharactersInUrl && !ALLOWED_TARGET_CHARACTER[next]) {
            throw new BadRequestException(UndertowMessages.MESSAGES.invalidCharacterInRequestTarget(next));
        }
    ………… ………… ………… 
    }
    

    可以看出,对于ASCII码在32-125之外的字符,是不允许的,直接抛出异常。对于该范围内的字符,有10个不允许出现。
    根据代码,可以解释上述中文的问题。由GBK编码规范可知,字符的第一个字节肯定大于127,不被服务器接受,所以返回了400错误。
    所以,问题并不是字符集导致的,而是%编码导致的。即使使用GBK编码中文字符,只要将字符的每个字节都转换为%HH的形式,依然能正确发送请求。至于可能导致的乱码问题,也有解决方式。但最好根据规范使用UTF-8。
    根据参考资料2显示,Tomcat7,8也严格执行了RFC 3986规范,阻止不安全字符。但我认为该文章对原因的分析是不准确的。

    Jboss下解决方案

    根据UNDERTOW-1185的ISSUE,Undertow在该版本做了更新,增加了一个开关选项 ALLOW_UNESCAPED_CHARACTERS_IN_URL ,当配置为true时,就不再根据规范限制url中的字符。相应地,Jboss eap根据JBEAP-13710的ISSUE,将在JBEAP-13744更新Undertow版本,预计会在Jboss-eap-7.1.1版本中见到新的配置项。
    但如果有条件,最好还是使用encodeUrl()编码一下,可以避免很多问题。与之相关的资料非常多,本文不再赘述。

    参考资料

    1. URL编码问题&乱码根源
    2. IE6-IE11 Get请求参数带中文tomcat 返回400错误并显示:Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

    博文发布在个人博客,欢迎访问!!

    相关文章

      网友评论

        本文标题:IE浏览器中文参数导致400 Bad Request问题研究

        本文链接:https://www.haomeiwen.com/subject/odfgzxtx.html