This application runs a HTTP server which should be deployed behind a reverse proxy (such as Apache HTTPD or NGINX) to enable HTTPS. The proxy should also be configured to block or redirect HTTP traffic.
Deploying to a platform-as-a-service will handle this automatically.
When deploying behind a proxy, you should set TRUST_PROXY=true
:
TRUST_PROXY=true ./index.js
(do not set this to true
unless behind a trusted proxy which sets
the X-Forwarded-*
headers)
All passwords are protected using the bcrypt hashing algorithm with a work factor, individual salts, and a "brute-force salt".
If you have a powerful webserver, you can increase the hash work factor:
PASSWORD_WORK_FACTOR=12 ./index.js
This value can be changed with each deployment (and should slowly increase over time as your deployment hardware becomes more powerful). Passwords which are weaker than the current value will automatically be re-hashed during successful logins. Lowering this value will only affect new passwords; existing passwords will never be rehashed to a weaker hash.
Increasing this value by 1 will double the time taken to perform a hash (assuming the same hardware). The default value (as of 2019) is 10. Due to the use of a brute-force salt, this has an effective average value of 12 for a successful login and 13 for an unsuccessful login.
For extra security against database breaches, you can also specify a secret pepper value. This protects passwords in the event of a database breach if the webserver itself is not compromised. The pepper value must be the same for all instances of the webserver, and must remain constant with deployments of new versions of the app. The best place to store this value is in a deployment pipeline configuration, or a configuration server.
PASSWORD_SECRET_PEPPER="asecretwhichmustnotbeknown" ./index.js
Currently it is not possible to cycle this secret value, as passwords can only be re-hashed during a successful login.
If this value ever changes, all passwords will become invalid. If you specify a secret pepper, ensure it will never be lost!
When launched with ./index.js
, node hardening flags are applied
automatically. If you need to customise the NodeJS flags, you should
be sure to specify these as well:
node --disable-proto delete index.js
Note that these must be included before the index.js
argument.
If you do not need to specify custom flags, it is recommended to
stick with using ./index.js
to launch the application instead of
node index.js
, as the former will automatically get new security
flags as they are added.
All retro item data is encrypted in the database using aes-256-cbc, regardless of database choice. By default, a secret key of all zeros is used, providing no real protection. To get the benefits of data encryption, supply a secret key on startup.
ENCRYPTION_SECRET_KEY="0000000000000000000000000000000000000000000000000000000000000000" ./index.js
The secret key should be 32 random bytes (256 bits) encoded in base16 (hex). You can generate a random key with:
./scripts/random-secrets.mjs
Non-item data (such as the retro name, settings, and current state) is not encrypted.
Currently it is not possible to cycle this secret value.
If this value ever changes, all retro data will be lost. If you specify a secret key, ensure it will never be lost!
All user and retro tokens are signed using the RSA256 algorithm. This requires a private key for signing, and a public key for verifying. The application will automatically generate these keys and store them in the database (a global pair for user tokens and a per-retro pair for retro tokens).
For extra security against database breaches, you can also specify a private key passphrase. This encrypts the private key, protecting your application from malicious tokens in the event of a database breach if the webserver itself is not compromised. The passphrase must be the same for all instances of the webserver, and must remain constant with deployments of new versions of the app. The best place to store this value is in a deployment pipeline configuration, or a configuration server.
TOKEN_SECRET_PASSPHRASE="asecretwhichmustnotbeknown" ./index.js
If this value ever changes, you will need to regenerate all key pairs. This will invalidate any active sessions, forcing all users to reauthenticate.
Before configuring authentication, you should create the database,
collections and indices necessary; the refacto
user will not have
permission to change these. You can do this by running the application
without exposing it to the internet; after startup the configuration
will be complete.
-
Create users:
use admin db.createUser({ user: 'admin', pwd: '<something secret>', roles: [ { role: 'userAdminAnyDatabase', db: 'admin' }, 'readWriteAnyDatabase', ], }); use refacto db.createUser({ user: 'refacto', pwd: '<another secret>', roles: [ { role: 'readWrite', db: 'refacto' }, ], });
-
Enforce authorization:
echo 'security.authorization: enabled' >> /usr/local/etc/mongod.conf brew services restart mongodb
You can now connect as the admin
user:
mongo --authenticationDatabase admin -u admin -p
And configure Refacto to connect as the refacto
user:
DB_URL="mongodb://refacto:<pass>@localhost:27017/refacto" ./index.js
https://docs.mongodb.com/manual/tutorial/enable-authentication/
To enable SSL (encrypted) communications, but without server or client identity checks:
# macOS
MONGO_VAR="/usr/local/var/mongodb"
MONGO_CONF="/usr/local/etc/mongod.conf"
# Ubuntu
MONGO_VAR="/var/mongodb"
MONGO_CONF="/etc/mongod.conf"
openssl req \
-x509 \
-newkey rsa:4096 \
-keyout "$MONGO_VAR/server-key.pem" \
-out "$MONGO_VAR/server-cert.pem" \
-subj "/C=/ST=/L=/O=/OU=/CN=localhost" \
-nodes \
-days 36500 \
-batch
cat \
"$MONGO_VAR/server-key.pem" \
"$MONGO_VAR/server-cert.pem" \
> "$MONGO_VAR/server.pem"
cat <<EOF >> "$MONGO_CONF"
net.ssl:
mode: requireSSL
PEMKeyFile: $MONGO_VAR/server.pem
EOF
# macOS
brew services restart mongodb
# Ubuntu
sudo service mongod restart
After enabling security, change the database URL when starting Refacto:
DB_URL="mongodb://localhost:27017/refacto?ssl=true" ./index.js
Note that after enabling this, unless you also configure identity checks, you will need to skip certificate validation when connecting via the commandline:
mongo --ssl --sslAllowInvalidCertificates
If the database is running on a separate server, you should enable client & server identity checks. The following resources offer guidance: