Log4j 2
  1. Log4j 2
  2. LOG4J2-439

Create a LogEventPatternConverter to escape newlines and HTML special characters

    Details

    • Type: New Feature New Feature
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.0-beta9
    • Fix Version/s: 2.0-rc2
    • Component/s: Layouts
    • Labels:
      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?

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

        Activity

          People

          • Assignee:
            Ralph Goers
            Reporter:
            Bruce Brouwer
          • Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development