I was trying to find the best way of utilising a config file in my projects, i.e. for reading in usernames, tokens, passwords etc.
I began by just using a config.json and then parsing that in another file to access the parameters. Then I came across this answer using a config.js instead.
But this got me thinking about the way environment variables can be accessed.
What is to stop some node package I download from grabbing my process.env
config.twitter.password = process.env.TWITTER_PASSWORD
and then firing off a HTTP request with it to some external database, thereby leaking any passwords/tokens I might have stored as environment variables?
I have a feeling I'm being both naive and also missing something important, so I'd appreciate some instruction. Thanks.
Related
When deploying a ReactJS + NodeJS application, what do you do about environment variables used on the React-side? Running NPM BUILD folds any secret API keys into the final code, making it visible to anyone who inspects the source code. Moving the .evn information to a .env file located server-side before using the npm build command doesn't solve the issue. I've tried to search around but I can't find any actual solutions.
Thanks!
You cannot make secret keys invisible on the client side. Even receiving it as an environment variable from the server, the keys will be incorporated into the build and will be publicly accessible.
So that your React app is not vulnerable, (correcting: not the only way) a possibility is to save keys and secrets in the backend. That is, the backend is the only one with access to keys and secrets. Client makes requests without storing sensitive data.
Thanks folks, all extremely helpful! I'll be moving the secret keys to the server side and changing the way my code works to compensate the adjustment. The other issue was Google Maps API, which can't be moved in the code but can be restricted to specific IP or URL use in the Google Cloud Platform.
https://cloud.google.com/blog/products/maps-platform/google-maps-platform-best-practices-restricting-api-keys
In order to avoid having secrets hard-coded into the code, I've used the standard approach of using dotenv to load an .env file when run locally, or have secrets injected into process.env in production.
However ... that got me thinking about malicious packages or stacktraces revealing the contents of process.env. And so exposing all the secrets. Given process.env is where everyone seems to store secret values, is there a risk of someone simply extracting process.env?
Is there a need to instead put the values somewhere else not so obvious? Like run over process.env, delete the values within it, and instead set up individual variables like MY_SECRET. Instead of process.env.MY_SECRET. I don't read any examples of this being recommended or done so I'd assume not (rolling my own, and all that!) but I wonder how exposed process.env is otherwise.
I'm relatively new to Node.js and Express and I was following a guide on adding authentication using JWT to my site found here: Medium. I'm confused by one point of this guide, about using secrets, that says:
This secret will be read by JWT library while creating and validating tokens. In production, we need to store this secret in environment variable instead of a file.
In the guide, they placed the secret inside a config file. I wish the guide described why exactly the JWT secret should be an environment variable instead of in a config file, but it's pretty vague. So why should secrets be environment variables, and what are other best practices for using secrets? I've decided to use Passport & Express Sessions for authentication instead of JWT, but I'd think it still applies to session secrets.
My next question is how would I exactly set a secret in an environment variable? I'm not using the guide anymore, but how would somebody that is using it convert that config file into an environment variable? And lastly, what do you usually use environment variables for in a typical Node.js app?
Why should a secret be an environment variable, instead of being stored in a config file?
While working on projects, you will end up uploading your code on github, which is accessible to everyone. If you store your secrets in a config file, anybody with a github account will be able to read it, and hence it is a security risk. Storing the secrets as environment variables ensure their safe-keeping. Your config file should read these values from the process.env object. Something like this:
const jwtSecretKey = process.env.JWT_SECRET_KEY;
const googleApiKey = process.env.GOOGLE_API_KEY;
const serverPort = process.env.PORT || 8000; // 8000 is the default value in case if the env variable has not been set
module.exports = {
jwtSecretKey: jwtSectetKey,
googleApiKey: googleApiKey,
serverPort: serverPort
}
And any other piece of code using these secrets should require the config file.
const config = require('./config');
...
jwt.verify(token, config.jwtSecretKey);
Along with storing secrets as environment variables, you should also store any environment specific values as environment variables. For example, say your NodeJS server (thats is connected to a hosted Database) is run on three environments - Development, QA and PROD. Each of these environments will have different information regarding the DB HOST and PORT, the environment should connect to. Environment Variables are a good way to store the DB Host and Port on each individual environment, and to use the same code across all environments to connect to different databases by reading the environment variable.
How to store the values as environment variables - One way of doing it is manually, though I wouldn't recommend it. One way is use of shell scripts. It actually depends upon your infrastructure.
You should also add your *.env to the .gitignore file So pushing it to the public will ignore the file and you have only one file with all the environment variables.
Maybe the original question was more about why you should not hard-code secrets into your codebase? Hopefully that is fairly obvious to most people. So ideally your secret and non secret "app config" should live separate from your codebase making your app "configurable". The standard way to do this:
have a development config file (e.g. config.js or .env) that is added to .gitignore so it is never checked in.
have a development sample config file (e.g. config.sample.js or sample.env) for new developers to use as a template for their own development configs (this is checked in and does not contain real secrets).
in production deployment this config file is generated or provisioned with production config and secrets by the devops team.
the path to the config file (location of file is not a secret) might be specified by an environment variable for the app process.
If you are concerned about security you won't put secrets into ENV.
(Yes, I know this is a controversial topic for some people. Sorry.)
You can't really prevent unprivileged users from getting access to ENV information. So if you put secrets in env, the bar is lowered for an attacker because they don't necessarily need a privileged account to see your app's secrets running under a different user.
if your app spawns any subprocesses, your secrets will be exposed to them because they inherit your environment.
if you have debug process logging at a system level, process ENV can be exposed to unprivileged users because the system doesn't consider ENV to be secret.
If you are provisioning a production server, you can just as easily put your secrets in a .env file as a systemd EnvironmentFile using the familiar environment variable format.
However the trick is you want to treat this as secret app-loaded config rather than loading it into process environment (via systemd, bash, npm scripts, etc) or process.env within your app.
dotenv by default (when you call .config()) populates process.env. so if you want to use dotenv securely, you can parse your .env config using dotenv.parse so it doesn't put the secrets from your .env into process.env.
This would allow you to provision a .env file with production secrets and use a development .env file that you add to .gitignore so you don't commit your development file. However application developers need to remember to use .parse instead of .config or else it is a security risk.
Alternatively if you don't want to use dotenv since its primary design is focused on loading up ENV... just use a json or js config file that exports a config object (e.g. config.js). Same as .env you can have a dev version that is not committed and a production one that is auto-provisioned. This can be required within the app where needed and avoids the risk (you have with dotenv) of accidentally populating secrets into the environment. Of course bad app code can always still leak secrets, but at least they won't be leaked by default due to how the secrets are exposed to the app.
The downside of all this is that ENV gave us a very standard way to pass config to any type of app/process/script (ie not just nodejs) in production environments. ENV being insecure means we have to come up with a (ideally standard) mechanism for passing secure config to a variety of apps and scripts in production.
In bash (as an example) you can source a .env file and as long as those vars aren't exported they are just "local" variables to the current bash process (ie not inherited by sub-processes). Not using export is akin to not populating process.env. Parsing json config in simple bash scripts...not so fun without helper apps.
You would like application developers to not have to think about production secrets/config or security and give them a standard config retrieval mechanism that works for local development, testing and production.
You would like this standard mechanism to be straight forward and relatively low-risk of them inadvertently opening up security issues on the app side (exposing production secrets to sub-processes). Of course nothing protects you 100% as if the app has access to the secret there are many ways it could leak it (writing to file, making http calls, etc)...but at least it wouldn't be due to the config loading/passing mechanism.
We are currently looking at options for addressing this for a variety of apps in production...so very happy if someone has well thought out suggestions or experience solving this in a clean, general purpose way.
I would be inclined to stick with the .env because we already support EnvironmentFile provisioning and every app can deal with that file format (e.g. can source a .env with bash)...the problem is how to prevent apps/developers from accidentally using dotenv.config instead of dotenv.parse to keep this out of the app environment.
So we are probably just sticking with config.sample.{env,json,js} so the devlops team has a template to populate with production secrets. Config is loaded by the node app via require so it doesn't populate process.env. We also are adding command-line flag (or ENV) to our apps to allow the location of this config file to be specified so devops has the option to place the production config file outside of the app deploy directory (which can make continuous deployment a bit easier). The downside is that a config.js won't work easily for other languages, but devops can fairly easily script converting an ENV style variable format into other targets.
As I said, keeping secrets out of your ENV is for people that are concerned about security. Many blogs/guides/tutorials online show secret passing through ENV. The popularity of dotenv which (by default) loads secrets into the process environment also might lead you to believe this is the right way. Security is always a tradeoff between risk, effort/cost and inconvenience. Personally I think if you auto-provision production it isn't a big deal to fix this...the issue is more about coming up with a semi-standard mechanism that can work across a variety of apps/code-languages.
References:
https://systemd-devel.freedesktop.narkive.com/ibRtoqPS/environment-variable-security
https://github.com/motdotla/dotenv#parse
https://github.com/motdotla/dotenv/pull/378
Background of the problem: I was trying to use dotenv to access environment variables to a Jekyll static site. (not reactjs frontend as most other similar questions)
I already followed all suggestions in this thread, with only one caveat: I'm not sure whether I added require('dotenv').config(); as early as possible because I use it in a <script> tag (using browserify for usage of require keyword) before body end.
But invocation like process.env.ENV_VAR returns undefined.
Anyone knows what could be the problem and how to solve it?
Realized I asked a quite stupid question.
From this discussion:
The .env variables are only accessible from the server side. If you
expose them on the client side then anyone who visits your page will
be able to have access to them so you probably wouldn’t want to do
that with things like passwords or secrets.
I am learning YUI and was wondering what is the best way to access my configurations (stored in a json) using YUI.
One way I came across was to maintain it in the config/app.json and access it with using global variable:
Y.Object.getValue(App, ['Cache', 'globals', 'context'])
Is this the best way? Also if my configuration is spread out over multiple json files, what would be the best way to access them?
Thanks
There are basically two ways to do this:
Include the configuration in the HTML page
Load the configuration using Ajax
Both have some pros and cons.
Including the configuration in the HTML
This requires you to do some server side coding that reads the JSON file and prints it in the page as a global variable. This is what you seem to be doing. The upside of this is that you don't have to make an extra HTTP request. The downside is that you're relying on global variables which can be fragile.
if you're using Node.js you can use express-state to expose that configuration to the client. Alternatively you can use express-yui which relies on a similar mechanism to generate YUI configuration.
Using Ajax
The downside of using Ajax is that it's slower, but the upside is that you can trust the information to be new and not have been modified by anything else in your page.
Dealing with multiple configuration files
My recommendation is that you merge the configuration into a single object. Just decide on some convention for which one wins and generate a single configuration object. This will simplify handling that information in the client. You can do this easily with express-state by just calling app.expose(config1); app.expose(config2) and so on.