Home > Software engineering >  How to avoid logging sensitive information from Java exceptions?
How to avoid logging sensitive information from Java exceptions?

Time:11-25

Java stacktraces might contain sensitive information (in my case - passwords), if the full stacktrace is logged by a logging framework (here Java Util Logging).

How to avoid logging such sensitive information without loosing other important information from the stacktrace in the log files?

Example:

package javalogspassword;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;

public class JavaLogsPassword {

    private static final Logger LOG = Logger.getLogger(JavaLogsPassword.class.getName());

    public static void main(String[] args) {

        Connection conn;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost/test?"
                      "user=root&password=");
            LOG.log(Level.FINER, "Connected");
            try (Statement statement = conn.createStatement()) {
                // this statement contains a syntax error
                statement.execute("ALTER USER test IDENT BY '*Choose-a-good-password*'");
            }
            conn.close();

        } catch (SQLException ex) {
            // password '*Choose-a-good-password*' will be logged
            LOG.throwing(JavaLogsPassword.class.getName(), "main", ex);
        }
    }
}

Logger configuration : logging.properties:

handlers= java.util.logging.FileHandler
.level= INFO
java.util.logging.FileHandler.pattern = java%u.log
javalogspassword.level = FINER

Command to run jar:

java -Djava.util.logging.config.file=logging.properties  -jar dist/JavaLogsPassword.jar 

The resulting file java0.log contains the password.

CodePudding user response:

There is no solution to this. Data is not black and white; the security sensitive nature of any piece of data is a shade of gray. Even seemingly entirely innocuous stuff like, say, a line number or method name in a stack trace is, theoretically, 'sensitive'. For example, it could lead one to surmise that you must be using version X.Y of library A.B, which has a known security leak, and I can now exploit this.

That's how security in real life works: It's rarely a single point of failure that can be conclusively pointed at as: THIS, this is the one and only reason the system got compromised. It's instead almost always multiple factors, each with a problem that on its own doesn't seem all that concerning, but put them all together, and voila.

Hence: There is no solution - log info is sensitive. It should be treated as such.

Of course, that sounds nice, but an actual plaintext password is black as night, and whilst all log info is neccessarily sensitive, there's a difference between 'carefully manage your logs' and 'just print passwords in it'.

There isn't a good solution there either, unfortunately: There are cultural factors at work (be very very careful about what you shove inside your exception messages and log statements. Whatever instinct made you go: Whatever, I'll put the entire SQL statement straight up inside a log file - address that), but cultural instincts are hard to instill and virtually impossible to test for (how do you unit test 'no line of the entire code base is at risk of logging passwords'? That's... decidedly non-trivial).

So, instead, it's more of a 'quench the fire when you see it': For this specific case you now know it, so fix things. In my experience, SQL queries do not as a rule of thumb include the entire statement, but if they do, use a PreparedStatement and set the password via .setString(1, ...) and they won't. This again goes to culture: In my dev teams, putting string literals in SQL statements is not acceptable, is often lint-checked for, and is aggressively policed via code review. Even if the string literal is known acceptable and entirely constant - use preparedstatements anyway. If the SQLException includes the entire statement including the values for the ? even when using preparedstatements, oof - go hunt in the settings of the JDBC driver if you can correct that. If you really can't, write code that strips the SQL statement out of exceptions (wrap .query, perhaps) - that's possible but complicated, I'd go look for alternatives first, though.

Other cultural aspects here involve not wanting any such direct sensitive information in any form that isn't by design extremely fleeting. In other words, having passwords in plain text in a database is the mistake - and that mistake then shows up in the form of 'oh oops it is really easy to spill that password via a log'. The fix isn't "prevent this from showing up in a log file", the fix is "do not store passwords unhashed in a database". Something as simple as encrypting it, even though the decryption password is right there in the codebase and therefore not a particularly well kept secret, already means that a developer reviewing the logs will need to actively commit to breaking their contracts (i.e. turn malicious, and if your programmers are malicious, this is the least of your problems, securitywise!) in order to actually know what that is.

Your dev team members can be bribed, and some will not be able to control their curiosity perhaps, but security is not black and white, it's shades of gray, and that would already help.

Even better is to find whatever process demands you have unhashed passwords and get rid of em, because there is just no reason to have these. If it's a password of a user in your system, what are you doing? Use bcrypt, scrypt, or some other password hash setup instead. If this is a password that you need in order to connect to external API, then talk to the external API provider and tell them their setup is not acceptable - this should be a matter of using JWTs or some other PKI authorization/authentication scheme instead of sending passwords about. And so on.

In other words, there are answers. But no fire-and-forget easily-applied cure-alls.

CodePudding user response:

The approach to do so is to

Research

Look into the logs and see what error types yielded sensitive information.

Modify

Implement a class that does the safe logging and delegate the logging of errors to this class whenever an error is catched that might contain sensitive information

Catch everything

You will need to make sure that any exception is catched and you handle the exceptions in the way that fits your needs the best.

Refactor

Look into each catch in your project and see whether there are potentially catching exceptions that might have sensitive information.

Wait and see

After you have completed the process, wait a while and then check the logs again and see whether there are cases that you have missed initially. If so, apply the same process for the cases that were not handled before.

  • Related