Skip to main content

Active vs. Passive Log4jShell remediation

 Log4jShell 

All computer professionals should be aware of the Log4jShell (CVE-2021-44228) and it follow on defects.  There is no shortage of opinions and lessons to be be learned:
  • The difficulty of performing safe interpretation
  • The problems when assumptions are not clearly documented.  I, for one, was completely shocked to find out that a logging system would actually attempt to do variable substitution in an actual message.
  • The difficulty of finding and resolving issues with such a common library that is not provided by an OS package manager.

IT'S A LOG4J CHRISTMAS

One of my favorite podcasts, Security Now - episode 850, discussed an analysis by Google regarding the depth of log4j dependencies.  From the show notes:

One contributing reason is because Log4j is, more often than not, an indirect dependency. Java libraries are built by writing some code which uses functions from other Java libraries, which are built by writing some code which uses functions from other Java libraries... and so on. That’s really how it happens. So, the problem is more often not that a Java library directly uses Log4j (though 17% do). It’s that the other 83% instead use another Java library which might use Log4j, or might use another library that does, and so on. And as a result of this multiple depth inheritance, maintainers of vulnerable Java packages are forced to wait until other developers and maintainers of the packages they use have updated their own libraries.

Consumer Focus Alert 

Steve's analysis assumes a passive approach to dealing with Java dependencies.  One option is indeed to wait for various packages to update to newer versions.  And, in some environments, it may be the right call, but that doesn't mean you should not explore additional options.

As a test, I took an old sample program I had that was coded against Apache Camel 2.23.0.  The maven pom.xml contains
...
  <dependencyManagement>
    <dependencies>
      <!-- Camel BOM -->
      <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-parent</artifactId>
        <version>2.23.0</version>
        <scope>import</scope>
        <type>pom</type>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>

    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-core</artifactId>
    </dependency>

    <!-- logging -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <scope>runtime</scope>
    </dependency>
...

This gives a dependency hierarchy of:
Note that the dependent version of log4j (from Camel) is 2.11.1 and is a vulnerable version.

Passive update

The passive approach would be to update Apache Camel.  If we do that, we could update to, say version 3.7.7.  If we change:
        <version>2.23.0</version>
to
        <version>3.7.7</version>
then we get a totally different dependency hierarchy (clipped) that shows a dependency now on version 2.16.0 of log4j:
As well as all of the restructuring that was done.  In my case, this application won't even compile without additional changes because one of the classes (org.apache.camel.main.Main) my sample depended on isn't available.

This means that I have to alter my code.  It's not a hard change, it's a simple StackOverflow search away showing my how to restructure the code.

But what if it isn't my code?  What happens if the problem is a library in a jar file that doesn't have a version available yet?  What if I want to use log4j 2.17.0 (because 2.16.0 also has problems)?

Active update

Rather than force an update to the top level libraries, we can exclude the "bad" lower level libraries ( log4j) and then specify the exact version we wish to use:
...
  <dependencyManagement>
    <dependencies>
      <!-- Camel BOM -->
      <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-parent</artifactId>
        <version>2.23.0</version>
        <scope>import</scope>
        <type>pom</type>
        <exclusions>
          <exclusion>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
          </exclusion>
        </exclusions>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>

    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-core</artifactId>
    </dependency>

    <!-- logging -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.17.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.17.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>2.17.0</version>
      <scope>runtime</scope>
    </dependency>
...


and then we get the dependency hierarchy we want:

Note that the 2.11.0 versions are omitted due to my overriding the excluded log4j 2.11.0 dependency and are replaced with log4j 2.17.0.

Analysis

The main reason to follow the active approach is time.  You don't have to wait for anyone else to release updates. On the other hand, there are disadvantages you need to factor into your decision:
  • You can only swap out versions if they are binary compatible with the JDK you are using.  With a mature product like log4j, compatibility is quite high, but that may not be the case for other libraries.
  • You now have a manual version override that you have to track.  Each and every time you change a version of the dependent library you MUST reconsider library versions.  At some point you will want to use a library that is dependent with a log4j version newer than 2.17.0.

Conclusion

Don't wait - if you are using Maven, please review https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html to explore your options.  

If you are using Gradle, the equivalent documentation is at https://docs.gradle.org/current/userguide/dependency_downgrade_and_exclude.html with a nice discussion at https://discuss.gradle.org/t/how-do-i-exclude-specific-transitive-dependencies-of-something-i-depend-on/17991




Comments

Popular posts from this blog

Spring Boot native builds when internet downloads are blocked made simple

 No direct access to the internet If you work at a company that controls their software bill of materials, it's quite common to be blocked from directly downloading from: Maven Central Docker hub GitHub (the public parts) Getting the bits Maven Maven is first, because without it, you won't be able to compile your Spring Boot application, let alone move on to turning it into a native docker image. I will be showing changes need to work with artifactory, but you should be able to adapt it to other mirror solutions.  repositories {   maven {     name = "central"     url = "https://artifactory.example.com/central"     credentials {       username = "${project.ext.properties.artifactory_username}"       password = "${project.ext.properties.artifactory_apikey}"     }   } } With this configuration change, you should be able to download your plugins and dependencies, allowing you to compile and ...

Kotlin Notebook when you're blocked from Maven Central

 TLDR; If you are blocked getting to maven central when first using Kotlin Notebooks because of company firewalls, you can use a tool like Fiddler Tool to redirect to a different network location. Kotlin Notebooks Kotlin Notebooks are a JDK based environment that brings the Python based Jupyter Notebooks  expressiveness to IntelliJ. From the blog post announcing the plugin, it looks like this: At home, the installation of jar files looked like this: I played around with it at home, but I couldn't use it at work.  Many companies, mine included, do not allow software components to be used when downloaded directly from the internet. In my companies case, we use a product called Artifactory, which allows you to mirror the content from Maven Central while still applying policies like CVE scanning, tracking, etc. The way it should work IntelliJ, as one of the leading IDE's, generally supports this quite well.  In fact, there is a whole setting page dedicated to dealing wi...