Secret values in Scala

geirolz

David Geirola

Posted on February 13, 2024

Secret values in Scala

Preface

One of the most important topics in IT is surely the security, it's so complicated and intriguing that, either you love it or you hate it, but we can't help taking care of it. Nowadays, in a society based on computers and applications, crackers attacks are very concrete and dangerous.  
In my opinion, when we talk about IT security, it is always better to overestimate the risk and have a large margin of tolerance. 
The (hard) trade-off we have to make is that the solution is usable, has good performance, and is safe.

Often, it is not a single solution that makes something safe; it is the sum of several approaches that mitigate the risk.


History

It has been a while since I had my first idea for a library that would help developers securely manage secret values within their applications. Originally, I started with a very simple wrapper of String to avoid unwanted printing or logging of the secret value, then I created my own application toolkit where I added a dedicated module with a minimal version of the Secret type. 

Step by step, iteration by iteration, I abandoned the toolkit module in favor of a dedicated library for handling secrets.
In this article, I want to briefly introduce you to my new library, explain how it works, and explain why you should use it in your projects. 
As I just said, this is the latest iteration of this idea, not the final one, and since I am now releasing it as a standalone library, I hope to get feedback and ideas to make it even better. 

Secret library github page
https://github.com/geirolz/secret

Read this interesting article which inspired my about this library
https://westonal.medium.com/protecting-strings-in-jvm-memory-84c365f8f01c


The problem of secrets in applications

I believe everyone who is reading this article already knows about these problems. 
Just to be pragmatic, I'll use one of my favorite tools, a list:

  • Hard-coded secrets
  • Unwanted printing and logging of secrets value
  • Memory leaks due to cracker attacks (e.g. JVM Heap dump)

I will briefly review these 3 problems to give you a broader context and to share the ideas behind my library. If you are not interested in this part, you can skip to the end of the article for the introduction to the library, or you can look directly at the library page on github.

Hard-coded secrets

Below is an image explaining how secure it is to have one's datacenter fenced and guarded by armed guards, a thousand layers of software security, and hard-coded secrets.
weak security

There are several approaches to avoid this bad practice; I won't go through them in this article, but just to give you a tip, if you are using Kubernetes, you could use Secrets; similar to ConfigMaps but specifically intended to hold confidential data. 

Then you can mount secrets as a volume, inject them as environment variables, or synchronously access the vault.

Let me know if you know more methods to solve this! 

Printing and logging

Unfortunately, this problem doesn't have a definitive solution. Common sense and education prevent most of the cases, but we are humans, and we can easily get distracted.

Image description

A very simple solution would be wrapping the secret value overriding the toString method.

case class Secret(value: String) extends AnyVal:
  override toString: String = "** SECRET **"
Enter fullscreen mode Exit fullscreen mode

Cool! But the following example could easily happen

case class Config(databasePass: Secret)
def initDb(pass: String): Unit = ???

//usage
val config: Config = ???
initDb(config.databasePass.value)
Enter fullscreen mode Exit fullscreen mode

The problem is that the initDb method has full access to the secret value, and someone could unintentionally log or print it. The easy solution would be to pass the type Secret instead of the String.

While one improvement for the Secret type might be to make the API a little more difficult to use to force the user to think.

class Secret(value: String):
  override toString: String = "** SECRET **"
  def use[T](f: String => T): T = f(value)
Enter fullscreen mode Exit fullscreen mode

This is not a solution; you can easily bypass it, but it makes it a little more difficult (sum of approaches). 

If you do this, you deserve a firing letter :)

val pass: String = secretPass.use(identity)
Enter fullscreen mode Exit fullscreen mode

Memory leaks

Ok, let's start with the assumption that if someone has access to your application environment, this is not the only problem you have, but surely we can make the lives of our friends crackers a bit harder.
Here we discuss the JVM heap dump attack, a common attack to steal secrets and sensible data from an application.

Basically, the attack consists of creating a snapshot of the JVM heap and analyzing it in order to find sensible information.

To work around this problem, we can use a kind of ByteBuffer called direct that allocates the data directly to the memory, outside the JVM heap.

ByteBuffer.allocateDirect(bytesCapacity)
Enter fullscreen mode Exit fullscreen mode

The heap will only contain the memory addresses of the byte buffer.

To improve security a bit, we can also obfuscate the bytes we are allocating by using some strategies, the most common of which is the use of the xor operator.
Xor is simple, fast, and isomorphic. Xor is not secure because it is not a cryptographic algorithm, but it is sufficient if we add up all these tricks.

Once finished with the secret byte buffer, you can overwrite the value with 0 and cancel the pointer to avoid unnecessary leaks. Secrets are usually used at startup to build connectors or instances; there is no need to keep them in memory forever unnecessarily.

Secret library

Well, I've created a library called Secret (very creative) that implements what you just read in a type named Secret.
It supports direct byte buffers, xor-ed values (all primitive types), secure destruction, map and flatMap.
With the special type DeferredSeret you can define a secret that is acquired and then destroyed whenever it is accessed (for example, a remote call to get the value from a vault).

It has integration modules for cats-effect, pureconfig, ciris and Typesafe Config

You can also extend it by defining a custom algorithm to use instead of xor (default). Check the documentation if you are interested.

As I wrote in the README, Secret library does its best to avoid leaking information in memory and in the code, BUT an attack is always possible, and I don't give any certainties or guarantees about security using this library; you use it at your own risk. The code is open-sourced; you can check the implementation and make your decision consciously. I'll do my best to improve the security and documentation of this project.

Let me know what you think about this article and this project! Comments, feedback, PRs, and discussions are welcome.
If you like the library, please consider leaving a star on Github to support it! 

💖 💪 🙅 🚩
geirolz
David Geirola

Posted on February 13, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Secret values in Scala
scala Secret values in Scala

February 13, 2024