Environment variables containing newlines in Node?
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?
Solution 1:
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:
dotenv supports newlines with double quotes:
double quoted values expand new lines
MULTILINE="new\nline"
becomes{MULTILINE: 'new line'}
Solution 3:
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 4:
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 asbash
,\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 toconsole.log()
indeed outputs newlines; e.g.,node -e 'console.log("ab\ncde")'
indeed outputs 2 lines.
- By contrast, JavaScript
-
PRIVATE_KEY="ab\ncde" node -p 'process.env["PRIVATE_KEY"]'
(literally) outputsab\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:
-
bash
,ksh
,zsh
: use an ANSI C-quoted string:
export PRIVATE_KEY=$'-----BEGIN RSA PRIVATE KEY-----\n....\n-----END RSA PRIVATE KEY-----'
- others, such as
dash
(and from any shell script that usessh
):
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-----'