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)
See Reverse Proxy in the services documentation for more details.
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.jsThis 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.jsCurrently 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 --force-node-api-uncaught-exceptions-policy --no-addons --disallow-code-generation-from-strings --disable-proto delete index.jsNote 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.jsThe secret key should be 32 random bytes (256 bits) encoded in base16 (hex). As a convenience, you can generate suggested random secrets with:
./index.js random-secrets
# or
docker run --rm refacto/refacto random-secretsExample output:
PASSWORD_SECRET_PEPPER=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
ENCRYPTION_SECRET_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
TOKEN_SECRET_PASSPHRASE=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV
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.jsIf this value ever changes, you will need to regenerate all key pairs. This will invalidate any active sessions, forcing all users to reauthenticate.
Refacto manages its own database, creating and modifying collections and indices
automatically. For this reason, it is best to configure mongo access with the
readWrite and dbAdmin roles within the database you want it to use.
These permissions are quite broad, but enabling user authentication is still beneficial for a number of reasons:
- Without authentication, any user on the machine is able to connect to MongoDB and perform any operation (and if MongoDB is exposed on a network, any machine which can reach the server can connect and perform any operation);
- Admin-level commands such as managing users and clusters can be restricted;
- Access to other databases hosted in the same MongoDB instance can be restricted.
To set up user roles from scratch in a MongoDB deployment:
-
Create an admin user: (this will be used to create the other users)
-
Pick a secure password for the admin user, and note it somewhere safe. For example:
openssl rand -base64 30 | tr '/+' '_-'
-
Create the user:
mongosh admin --eval 'db.createUser({user:"my-admin-user",pwd:passwordPrompt(),roles:["root"],mechanisms:["SCRAM-SHA-256"]})';
Replace
my-admin-userwith the username you want to use.Note: if you want to specify the password programmatically rather than entering it manually, you can pipe the password to the
mongoshcommand viastdin.
-
-
Create a refacto user with access to the specific database you plan to use:
mongosh my-refacto-db --authenticationDatabase=admin -u my-admin-user -p \ --eval 'db.createUser({user:"my-refacto-user",pwd:passwordPrompt(),roles:[{"db":"my-refacto-db","role":"readWrite"},{"db":"my-refacto-db","role":"dbAdmin"}],mechanisms:["SCRAM-SHA-256"]})';
Replace all occurrences of
my-refacto-dbwith the database name you are using, and replacemy-refacto-userwith the username you want to use.Note: if you want to specify the passwords programmatically rather than entering them manually, you can pipe the passwords to the
mongoshcommand viastdin, separated by newlines (admin password + newline + refacto user password). -
Enforce authorization:
Check the location of your
mongod.conffile (typical locations) and modify it to enable authorization:echo 'security.authorization: enabled' >> /etc/mongod.conf
Restart MongoDB: (the exact command will depend on your system and how you installed it)
sudo systemctl restart mongod
You can now connect as the my-admin-user user:
mongosh --authenticationDatabase admin -u my-admin-user -pAnd configure Refacto to connect as the my-refacto-user user:
DB_URL="mongodb://my-refacto-user:<pass>@localhost:27017/my-refacto-db" ./index.jshttps://docs.mongodb.com/manual/tutorial/enable-authentication/
If the database is running on a separate server, you should enable encrypted communication (to avoid eavesdropping) as well as client and server identity checks (to avoid man-in-the-middle attacks). See the MongoDB documentation for guidance.
By default, Refacto will log server and client errors, but will not record events or any client details (platform / browser version).
Server-side errors are always logged.
You can enable more detailed logging when starting the server if desired:
ANALYTICS_EVENT_DETAIL="version" \
ANALYTICS_CLIENT_ERROR_DETAIL="version" \
./index.jsBy default, the event detail is set to none, and client error detail is set to
message. The possible values for both options are:
-
version: Record the event or error, the name of the platform, and the name and major version of the browser. -
brand: Record the event or error, and the name of the platform and browser (but do not include version information). -
message: Record the event or error (and a timestamp), but do not record any details about the platform or browser which is in use. Note that some error messages may identify the browser implicitly. -
none: Do not record this data at all.
None of these settings involve the use of cookies to track users. Only the
User-Agent header of requests is used.
Any users who set the DNT (Do Not Track) or Sec-GPC (Global Privacy Control)
header will not be included in logs (as if ANALYTICS_EVENT_DETAIL were limited
to none and ANALYTICS_CLIENT_ERROR_DETAIL were limited to message or
none). Removing these users from the logs goes beyond required privacy
controls (as the details recorded are not shared with third parties), but is
done to respect user preferences.