Uploaded image for project: 'Log4j 2'
  1. Log4j 2
  2. LOG4J2-439

Create a LogEventPatternConverter to escape newlines and HTML special characters

    XMLWordPrintableJSON

Details

    • New Feature
    • Status: Closed
    • Major
    • Resolution: Fixed
    • 2.0-beta9
    • 2.0-rc2
    • Layouts
    • None

    Description

      To prevent log forging and HTML based attacks from viewing logs in a browser, we could add a LogEventPatternConverter that escapes newlines and HTML special characters. ESAPI has a method to do this, but it doesn't have any of the nice API features that Log4j 2 has.

      I was able to create a LogEventPatternConverter to do this. Note, this is only a proof of concept. I didn't try to exhaustively list all the special characters that might need to be replaced. I also didn't provide any configuration so we could choose to not escape HTML, for example.

      With this configuration:

      <PatternLayout pattern="%d %-5p [%t] %c %encode{%m}%n"/>
      

      And logging this message:

      LOG.warn("hi\n & <h1> there");
      

      Would result in this being logged:

      2013-10-28 16:31:21,606 WARN  [main] example.Test hi\n &amp; &lt;h1&gt; there 
      

      instead of this (which shows the potential for log forging):

      2013-10-28 16:31:21,606 WARN  [main] example.Test hi
      & <h1> there 
      

      This is roughly the code I used:

      @Plugin(name = "escape", category = "Converter")
      @ConverterKeys({ "escape" })
      public final class EscapingReplacementConverter extends LogEventPatternConverter {
      
          private final List<PatternFormatter> formatters;
      
          private EscapingReplacementConverter(final List<PatternFormatter> formatters) {
              super("escape", "escape");
              this.formatters = formatters;
          }
      
          public static EscapingReplacementConverter newInstance(final Configuration config,
                                                                 final String[] options) {
              if (options.length != 1) {
                  LOGGER.error("Incorrect number of options on escape. Expected 1, received "
                          + options.length);
                  return null;
              }
              if (options[0] == null) {
                  LOGGER.error("No pattern supplied on escape");
                  return null;
              }
              final PatternParser parser = PatternLayout.createPatternParser(config);
              final List<PatternFormatter> formatters = parser.parse(options[0]);
              return new EscapingReplacementConverter(formatters);
          }
      
          @Override
          public void format(final LogEvent event, final StringBuilder toAppendTo) {
              final StringBuilder buf = new StringBuilder();
              for (final PatternFormatter formatter : formatters) {
                  formatter.format(event, buf);
              }
              toAppendTo.append(buf.toString()
                                   .replaceAll("\\r", "\\\\r")
                                   .replaceAll("\\n", "\\\\n")
                                   .replaceAll("&", "&amp;")
                                   .replaceAll("<", "&lt;")
                                   .replaceAll(">", "&gt;"));
          }
      }
      

      If this sounds good, I would like to hear feedback and ideas on how to make this better. I will then contribute this to the project. Do you think this could this get in 2.0?

      Attachments

        1. EncodingPatternConverter.patch
          6 kB
          Bruce Brouwer
        2. log4j2-439-doc.patch
          2 kB
          Bruce Brouwer

        Issue Links

          Activity

            People

              rgoers Ralph Goers
              bruce.brouwer Bruce Brouwer
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: