- 起因
有一个数据上报接口,之前在物理机上部署,数据上报正常。
最近将项目迁移到 docker 中,结果出现了异常如下:
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
at org.apache.coyote.http11.InternalInputBuffer.parseRequestLine(InternalInputBuffer.java:192)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1028)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
- 问题原因
问题排查过程:略
原因:请求中出现了字符"{"(示例请求)
https://127.0.0.1:8080/metric?data=[{%22app
在不更改接口数据的前提下,解决方案为:在tomcat的cataline.properties追加配置
tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}
官方文档链接:http://tomcat.apache.org/tomcat-8.0-doc/config/systemprops.html#Other
tomcat.util.http.parser.HttpParser. requestTargetAllow | A string comprised of characters the server should allow even when they are not encoded. These characters would normally result in a 400 status. The acceptable characters for this property are: WARNING: Use of this option will expose the server to CVE-2016-6816. If not specified, the default value of |
- 问题来源
那么为什么之前接口不会出现异常呢?是tomcat版本不同导致的。
查看源码后发现:org.apache.tomcat.util.http.parser.HttpParser
tomcat 8.2.3 版本及 tomcat 7.0.82 ,都有如下代码,读取配置
String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow");
if (prop != null) {
for (int i = 0; i < prop.length(); i++) {
char c = prop.charAt(i);
if (c == '{' || c == '}' || c == '|') {
REQUEST_TARGET_ALLOW[c] = true;
} else {
log.warn(sm.getString("httpparser.invalidRequestTargetCharacter",
Character.valueOf(c)));
}
}
}
而tomcat 8.0.14 版本中并没有读取配置,对 |
{
}
的处理,而是默认为合法字符。
static {
// Setup the flag arrays
for (int i = 0; i < 128; i++) {
if (i < 32) {
isToken[i] = false;
} else if (i == '(' || i == ')' || i == '<' || i == '>' || i == '@' ||
i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' ||
i == '/' || i == '[' || i == ']' || i == '?' || i == '=' ||
i == '{' || i == '}' || i == ' ' || i == '\t') {
isToken[i] = false;
} else {
isToken[i] = true;
}
if (i >= '0' && i <= '9' || i >= 'A' && i <= 'F' ||
i >= 'a' && i <= 'f') {
isHex[i] = true;
} else {
isHex[i] = false;
}
}
}
虽然没有进行全面的比对,但可以看出在 8.0.x 左右的一些版本中,tomcat.util.http.parser.HttpParser. requestTargetAllow
这个配置是没有生效的,即 |
{
}
这3个符号认为是合法的。
- 结论:
tomcat.util.http.parser.HttpParser. requestTargetAllow
这个字段可以解决URL中存在|
{
}
字符的问题。- tomcat自tomcat 8.0.35版本之后对URL参数做了比较规范的限制,必须遵循RFC 7230 and RFC 3986规范,对于非保留字字符(json格式的请求参数)必须做转义操作。