I am running a spring boot application with an embedded Tomcat server on an AWS EC2 instance. I use Log4J2
to log both the spring boot logs as well as the tomcat server logs.
Tomcat will usually display a log that includes the ip address of the computer that made the request, the endpoint that was requested and how long it took to respond. But this only happens when the application is able to process the request and send a response.
If the request can't be processed because the request header can't be parsed then the Http11Processor
will throw an error that is logged but that does not include an ip address.
The log looks like this:
[INFO ] 2022-08-06 22:37:26.363 [http-nio-8080-exec-1] Http11Processor
- Error parsing HTTP request header Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level. java.lang.IllegalArgumentException: Invalid character found in the request target [/index.php?s=/Index/\think\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=HelloThinkPHP21 ]. The valid characters are defined in RFC 7230 and RFC 3986 at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:494) ~[tomcat-embed-core-9.0.65.jar!/:?] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:271) ~[tomcat-embed-core-9.0.65.jar!/:?] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.65.jar!/:?] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) ~[tomcat-embed-core-9.0.65.jar!/:?] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) ~[tomcat-embed-core-9.0.65.jar!/:?] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.65.jar!/:?] at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.65.jar!/:?] at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.65.jar!/:?] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.65.jar!/:?] at java.lang.Thread.run(Thread.java:833) ~[?:?]
This error was caused by a bot that was scanning my server for php
related vulnerabilities. I want to block this bot but i need the ip address to do that and http11Processor
doesn't log that by default.
So i want to catch this exception and log the ip address in the catch clause. My question is: Where should i place this catch clause?
I log the tomcat server logs using a custom AccessLogValve
that looks like this:
public class Log4JAccessValve extends AbstractAccessLogValve {
private static final Logger httpAccessLogLogger = LogManager.getLogger("http_access_log");
@Override //https://stackoverflow.com/questions/40172395/how-to-add-an-accesslogvalve-to-tomcat-programmatically
protected void log(CharArrayWriter message) {
httpAccessLogLogger.info(message.toString());
}
}
But i don't think the http11Processor
passed through this AccessLogValve
or else the log would have mentioned http_access_log
instead of http-nio-8080-exec-1
. How do i catch the exceptions coming from Http11Processor
so that i can alter the error message?
CodePudding user response:
Tomcat is quite strict in respecting the HTTP RFCs, most other software (including major browsers) is not.
As you observed correctly the invalid requests are not logged by your AccessLogValve
, because they never leave Tomcat's HTTP server (Coyote). If you want to log those requests, you need to relax Tomcat's URI validator (as e.g. in this question), so that they can be forwarded to Tomcat's valves:
<Connector port="..." ...
relaxedPathChars=""<>[\]^`{|}"
relaxedQueryChars=""<>[\]^`{|}"/>
Remark: there are so many bots scanning for vulnerabilities, that I would not bother increasing Tomcat CPU usage just to log their IPs and block them.
CodePudding user response:
There is a non-recommended but effective way that you can customize a Valve
and get the Throwable
with the following code.
Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
Refer to ErrorReportValve
implementation.