Environment variables containing newlines in Node?

node.jsEscapingEnvironment Variables

node.js Problem Overview


I'm attempting to load an RSA private key into my nodejs application using environment variables, but the newlines seem to be being auto-escaped.

For the following, assume that the PRIVATE_KEY env var is set to the following (not my actual key):

PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp\nwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5\n1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh\n3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2\npIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX\nGukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il\nAkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF\nL0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k\nX6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl\nU9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ\n37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=\n-----END RSA PRIVATE KEY-----"

If I call console.log directly with the preceding string, I get the following as output:

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----

You can see that the newline chars are being respected. However, if I call console.log(process.env["PRIVATE_KEY"]), the output contains the \n literal instead of actual newlines:

-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp\nwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5\n1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh\n3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2\npIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX\nGukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il\nAkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF\nL0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k\nX6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl\nU9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ\n37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=\n-----END RSA PRIVATE KEY-----

I've been trying to find more information online about how node processes environment variables with no luck. How do I load this key via env vars while maintaining newline characters? If that's not possible, how can I restore them?

node.js Solutions


Solution 1 - node.js

Just replace \n before use the value:

var private_value = process.env.PRIVATE_KEY.replace(/\\n/g, '\n');
console.log(private_value);

will result correctly:

-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----

Solution 2 - node.js

If you're using dotenv: We solved it this way, with newlines and JSON.parse (this allows any backslash escaped chars within the string, not just \n):

in .env:

MY_KEY='-----BEGIN CERTIFICATE-----\nabcde...'

in server.ts:

myKey = JSON.parse(`"${process.env.MY_KEY}"`), // convert special chars

See thread: https://github.com/motdotla/dotenv/issues/218

Solution 3 - node.js

dotenv supports newlines with double quotes:

> double quoted values expand new lines > > MULTILINE="new\nline" becomes > {MULTILINE: 'new line'} >

Solution 4 - node.js

Node.js does correctly reflect environment variables that have embedded actual newlines, as the following bash snippet demonstrates:

$ PRIVATE_KEY=$'ab\ncde' node -p 'process.env["PRIVATE_KEY"].indexOf("\n")'
2  # 0-based index of the (first) actual newline char. in env. var. 'PRIVATE_KEY'

Note that $'...' is a special type of bash string in which escape sequences such as \n are expanded, so in the command above PRIVATE_KEY is indeed defined with 2 lines and passed as an environment variable to node (simply by prepending the variable assignment to the command to invoke, which is a standard feature in POSIX-like shells).

A simple way to print the value of an environment variable verbatim is:

# With *verbatim* '\n'
$ X='line 1\nline 2' node -p 'process.env.X'
line1\nline 2

# With *actual newline*, thanks to $'...' interpolation.
$ X=$'line 1\nline 2' node -p 'process.env.X'
line 1
line 2

In fact, Node doesn't interpret the value of environment variables in any way (which is the right thing to do).

It must be that your PRIVATE_KEY variable doesn't contain actual newlines, but \n literals (a \ char. followed by char. n).

  • If the assignment command PRIVATE_KEY="..." in the question is a shell command, that would explain it: In POSIX-like shells such as bash, \n inside a "..." string is left as-is.

    • By contrast, JavaScript "..." strings do interpolate escape sequences such as \n, which is why passing such a string directly to console.log() indeed outputs newlines; e.g., node -e 'console.log("ab\ncde")' indeed outputs 2 lines.
  • PRIVATE_KEY="ab\ncde" node -p 'process.env["PRIVATE_KEY"]' (literally) outputs ab\ncde, showing that \n was retained as a literal.

You have two options to fix your problem:

  • Preferably, define your environment variable with actual newlines to begin with - see below.

  • Alternatively, if you do not control how the environment variable is set, expand the \n literals to actual newlines in your Node.js (JavaScript) code: see Adriano Godoy's helpful answer.


To define PRIVATE_KEY with actual newlines in POSIX-like shells, use one of the following techniques:

export PRIVATE_KEY=$'-----BEGIN RSA PRIVATE KEY-----\n....\n-----END RSA PRIVATE KEY-----'
  • others, such as dash (and from any shell script that uses sh):

export PRIVATE_KEY="$(printf %s '-----BEGIN RSA PRIVATE KEY-----\n....\n-----END RSA PRIVATE KEY-----')"

Alternatively, for better readability you can use a command substitution with a here-document:

export PRIVATE_KEY="$(cat <<'EOF'
-----BEGIN RSA PRIVATE KEY-----
...
...
-----END RSA PRIVATE KEY-----
EOF
)"

Finally, as Allen Luce's answer shows, POSIX-compatible shells allow even regular string literals to span multiple lines, though readability may suffer a bit:

export PRIVATE_KEY='-----BEGIN RSA PRIVATE KEY-----
...
...
-----END RSA PRIVATE KEY-----'

Solution 5 - node.js

It may be easier to see what's actually going on by inspecting rather than printing:

% export X="hey\nman"
% node
> process.env['X']
'hey\\nman'
>

Node's REPL implicitly applies the util.inspect method which emits an escaped string. That shows the literal backslash and lower-case "n" that the environment variable contains. So what's being fed to console.log is more like this:

> console.log("hey\\nman")
hey\nman

One option (in most popular shells) is to embed newlines straight into a variable just by hitting enter within a quoted string:

% export X="hey
> man"
% node
> process.env['X']
'hey\nman'
> console.log(process.env['X'])
hey
man
undefined
>

This technique also works within script files, just use newlines within quotes. If you have to take the environmental variables as-is a common approach is use string replacement:

% export X="hey\nman\ndude\nwhat"
% node
> console.log(process.env['X'].replace(/\\n/g, '\n'))
hey
man
dude
what

Solution 6 - node.js

As described in another topic you can encode yout multiline string in base64 and put it inside .env like

HTTPS_CA_CERTIFICATE=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURDVENDQWZHZ0F3SUJBZ0lVY29lWFp1R

And then decode it inside your app

var cert = new Buffer(process.env.HTTPS_CA_CERTIFICATE, 'base64').toString('ascii');

Solution 7 - node.js

I was using:

JSON.parse(`"${process.env.PUBPEM}"`)

which suddenly started to fail. So I changed it to:

JSON.parse(JSON.stringify(process.env.PUBPEM))

Which solved it for me.

Solution 8 - node.js

For the benefit of anyone out there trying to use multi-line variable (i.e RSA private keys) in a dotenv file (should work for any configuration file though).

  1. make each line of the private key end with a \n

    such that first line**\n** second line**\n** and the third line and so on

  2. now make the variable in single line in double quotes as follow KEY="such that first line**\nsecond line\nand the third line and so on\n**"

  3. Ensure you add a leading and trailing \n character for the key as well KEY="\nsuch that first line**\nsecond line\nand the third line and so on\n**"

  4. now in your code (i.e node, of course if you do not have access to code you have rely on the behaviour of the platform/app)

goodlyRepresentedKey = (process.env.KEY).replace(/\n/g, '\n');

(if biggly why not goodly)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLogan FullerView Question on Stackoverflow
Solution 1 - node.jsAdriano GodoyView Answer on Stackoverflow
Solution 2 - node.jsFreewalkerView Answer on Stackoverflow
Solution 3 - node.jsJBallinView Answer on Stackoverflow
Solution 4 - node.jsmklement0View Answer on Stackoverflow
Solution 5 - node.jsAllen LuceView Answer on Stackoverflow
Solution 6 - node.jsBitZarView Answer on Stackoverflow
Solution 7 - node.jsspa900View Answer on Stackoverflow
Solution 8 - node.jsSenthu SivasambuView Answer on Stackoverflow