I’m reading some of the secret rules descriptions, and something looks strange to me: you seem to officially recommend to store secrets in environment variables.
Typically:
Code examples
Noncompliant code example
props.set(“vercel_api_access_token”, “EFvQS4yzzxia31Hg5xmy6uOn”) // Noncompliant
Compliant solution
props.set(“vercel_api_access_token”, System.getenv(“VERCEL_API_ACCESS_TOKEN”))
By nature, methods like System.getenv
are available to every single part of the executed application, including imported dependencies. It is not possible to know if one of the dependencies - direct or transitive - tries to access well known environment keys, like VERCEL_API_ACCESS_TOKEN
.
Isn’t it dangerous to officially recommend to store secrets in environment variables?
Wouldn’t it be more accurate to recommend to pass them as arguments to the executable, and have the main
method store them in memory, and clean the passed args
, so that they are not accessible from this point forward?
Hello Philippe,
thank you for raising your concern in our community forum.
If I understand correctly, you are concerned that a malicious dependency that can execute arbitrary (Java) code in the context of the application can obtain secrets passed as environment variables more easily than if these secrets would be passed as command line arguments.
While it is true, that System.getenv
can be used to obtain set environment variables, a malicious dependency could also use System.getProperty("sun.java.command");
to obtain the passed command line arguments.
So, given this attacker scenario I do not see any difference.
If you want to go for extra security you can indeed try to use some techniques that limit the time when a secret is stored in memory. However, these techniques are usually not necessary for the most scenarios and therefore we suggest setting secrets via environment variables since this will be sufficient for most users.
You can also read more about secrets management in this cheat sheet by OWASP: Secrets Management - OWASP Cheat Sheet Series
I hope my explanation helps,
Daniel
3 Likes
True. But I’m not refering to the JVM only. The rule that I mention, except if I miss something obvious, is runtime agnostic. But the compliant solution is opinionated towards environment variables.
Typically, with Node.js, it is possible to:
have the main
method store them in memory, and clean the passed args
In my opinion, this should be the proposed compliant solution for every runtime that allows cleaning the received arguments.
In the case of Node.js, even process.env
can be cleaned, so this is another option.
console.log(process.argv); // [ '/usr/local/bin/node', 'index.js' ]
console.log(process.env); // {...gazillons of things}
process.argv = [];
process.env = {};
console.log(process.argv);// []
console.log(process.env); // {}
But, environment variables are available to every single process that runs in a given host. And we can’t do anything against that. Cleaning them in the entry point of one specific application doesn’t make them less accessible to all the others. They are out of control.
Storing sensitive data in environment variables is basically admitting that we put those sensitive data at the disposal of every single process. This is not secure.