Abstract: When the Web project is large and has a lot of historical code, you should use the log4J2 framework’s ability to modify log injection issues, rather than following the step-by-step evolution of parameters described in some blog posts.

This article is from huawei Cloud community “Java Cloud Service Development security Issues analysis — Log Injection, Not so easy”.

Case story

A new system online, small A in which developed A simple login module, will log in the record of all successful or failed users.

Small A has whitelisted all the user names. Incorrect names will also be printed out in the form of WARN for record.

Something like this:

[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
[2021-04-17 16:50:35][WARN][main] [Login:308] username is wrong,userName=tony.dssdff
Copy the code

Logs are connected to a risk audit system that periodically audits those who log in with suspicious daily behavior, such as those who log in late at night or log in frequently (never mind the details, you can do this without auditing, just for example).

One day, the log audit system indicates that Tony logs in too frequently and the operation is risky, so Tony’s number is blocked.

Then a day and sealed N more innocent users, causing a large number of users dissatisfied. Operation department to find guilty, small A took out the following log file as evidence:

[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
[2021-04-17 16:50:35][WARN][main] [Login:308] username is wrong,userName=tony.dssdff
[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
Copy the code

Tony: I was traveling that day. I left my computer at home.

Username = ‘username’; username = ‘username’; username = ‘username’;

username=tony.dssdff
[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
Copy the code

Username = ‘username’; username = ‘username’; username = ‘username’; (Because the sender may be using RESTFUL API to send malicious messages, so it also bypassed the verification of the foreground page)

As A result, Little A’s company suffered huge losses and little A eventually lost his job.

Simple rectification method

Little A struggled to find A new company to take over A pile of old code. He decided to take the precaution of adding line breaks to the externally entered log parameters.

He wrote a method as follows:

/** * get the purified message, filter the line, Public static String getCleanedMsg(String Message) {if (message == null) {return ""; } message = message.replace('\n', '_').replace('\r', '_'); return message; }Copy the code

And to log their own place, supplement this method

LOGGER.warn("username is wrong,userName={}", getCleanedMsg(userName));
Copy the code

But I remembered that the system was old and there were many similar parameters, so I searched and found that there were more than 1,000 logs with parameters, many of which were left by his predecessors.

Therefore, with a sense of responsibility, he modified and checked one by one. After more than a month, he finally checked out all external input parameters and added getCLeanMsg method. At the end of the year, due to insufficient output, the lowest performance, depressed, hair fell out again.

Log4j2 Config unified modify message

Small A was changed to A project team, this time decided not to repeat the mistake, use another way to simplify. In his project, logs are printed using log4j2, and it would be nice to take advantage of the framework’s ability to remove all newlines from the logs, strictly ensuring that only 1 line is output.

So I began to study the official documentation of log4j2. He found a position in it related to the log output format, as follows:

Logging.apache.org/log4j/2.x/m…

He searched for the keyword \n, or newline, and found the following:

%enc{%m}{CRLF} is used to filter this part of the line. Log4j2.xml is changed to the following:

        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}][%-5p] [%t] [%c{10}#%M:%L] %enc{%m}{CRLF} %n "/>
        </Console>
Copy the code

Test, and eventually all logs will be one line long. Logs that used to cause problems are now

username=tony.dssdff\r\n[2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony
Copy the code

Therefore, it will not be incorrectly resolved by the log system, and at the same time, the risk of one investigation is eliminated.

Log4j2 modifies the mesage in the exception

After a month, suddenly the log audit alarm again, and finally down the false positive. I went to the log, and it looked like this:

[2021-04-17 16:50:35][INFO][main] [Login:308] unknown error happend java.lang.RuntimeException: name,name=%s [2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony [2021-04-17 16:50:35][INFO][main]  [Login:308] login success,userName=tony [2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony [2021-04-17 16:50:35][INFO][main] [Login:308] login success,userName=tony at java.net.SocketInputStream.socketRead0(Native Method) ~[?:?] at java.net.SocketInputStream.socketRead(SocketInputStream.java:115) ~[?:?] at java.net.SocketInputStream.read(SocketInputStream.java:168) ~[?:?] at java.net.SocketInputStream.read(SocketInputStream.java:140) ~[?:?] at sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:448) ~[?:?]Copy the code

Boy, it turns out that some places print out the unhandled exception stack when printing the log. The first line of the exception stack is usually the exception name +message, which can also be maliciously attacked. Small A rummaged through the log4j2 document, can not find in exception handling newline symbol, found only one ThrowablePatternConverter, told him in A document, you can customize the ThrowablePatternConverter, to print what you want.

So he wrote a UndefineThrowablePatternConvert, rewrite the log inside the stack print logic,

/** * a format conversion class that does specific encoding for exceptions * Added in the layout can be * * % eEx @ since 2021/4/16 * / @ Plugin (name = "UndefineThrowablePatternConverter", Category = PatternConverter. Category) / / layout of the definition of their key @ ConverterKeys ({" uEx "}) public class UndefineThrowablePatternConverter extends ThrowablePatternConverter {/ * * * for handling a specific coding ThrowableProxy * / static class EncodeThrowableProxy extends ThrowableProxy { public EncodeThrowableProxy(Throwable throwable) { super(throwable); } // encode \r and \n, @override public String getMessage() {String encodeMessage = super.getMessage().replaceAll("\r", "\\\\r").replaceAll("\n", "\\\\n"); return encodeMessage; } } protected UndefineThrowablePatternConverter(Configuration config, String[] options) { super("UndefineThrowable", "throwable", options, config); } // Log4j2 is constructed using a reflection call to the newInstance static method, so it must be implemented. public static UndefineThrowablePatternConverter newInstance(final Configuration config, final String[] options) { return new UndefineThrowablePatternConverter(config, options); } @Override public void format(final LogEvent event, final StringBuilder toAppendTo) { Throwable throwable = event.getThrown(); if (throwable == null) { return; } // Use custom EncodeThrowableProxy, EncodeThrowableProxy proxy = new EncodeThrowableProxy(throwable); / / added to toAppendTo proxy. FormatExtendedStackTraceTo (toAppendTo options. GetIgnorePackages (), the options. GetTextRenderer (), getSuffix(event), options.getSeparator()); }}Copy the code

And adding %uEx to PatternLayout will use the format here to generate the stack string.

conclusion

  • Whitelists are not immune to log injection problems, because sometimes we may log input parameters that have errors.

  • When the Web project is large and has a lot of historical code, you should use the log4J2 framework’s ability to modify log injection issues, rather than the case-by-case evolution of parameters described in some blog posts

  • Messages in the exception stack also have log injection risk, and if the project supports print stacks, it is best to handle this as well.

Click to follow, the first time to learn about Huawei cloud fresh technology ~