Secrets and stack traces

Wed 2 Nov 2022

This article is going to be a small reminder. It is going to point out some obvious best practices. It is always good to check these kinds of behaviours for yourself.

I've looked at some logs today and found out that some credentials were printed as plain text. I dug into this and found that the stack trace of an exception was passed on to Laravel's logger. Laravel's logger uses the var_export() function to dump this information to log when it finds an array passed along. It so happens that var_export() has the same behavioral traits as var_dump(). This means that it will dump the information regardless of their scope. Resulting to credentials leaking to logs when you pass along an $exception->getTrace() to the logger.

Now. Let me sum up some best practices.

  • It is best practice not to log your stack traces
  • Stack traces should be disabled on production environments
  • When it becomes a wish to see trace information; use the proper function for it. $exception->getTraceAsString() that sanitizes scoped data
  • Be aware of var_dump() and var_exports() behaviour, especially if data is passed along in your code and by extension vendor code
  • You could use paragonie/hidden-string to obscure secrets more

Running the test

I've made a couple scripts that help you test this behaviour. They are available through gists. Straight up run these and see for yourself.

Secrets as arguments

Secrets as arguments are always readable as a dump. It would be better to follow the config structure and create objects that hold these secrets scoped. Then call the methods and accessing those properties directly or through getters instead of getting sensitive data as arguments.

paragonie/hidden-string

While Paragonie's library will help you hide secrets even more, it doesn't protect you against the var_export() info leak when you pass along, lets say like in my case, a stack trace. Though it is a good to use things like this to obscure secrets in your application.

Another hack to encapsulate your secret by either and array or dedicated class for that as this library is part of paragonie/halite. The requirements of this lib might not align with all projects. So, you could do something like the following to achieve the same thing (when it comes to stack trace info dumping).

Closing words

When you follow best practices it's hard to fall in these pitfalls to be honest. It is good to be aware that stuff like this can happen. That's why I wrote this article.