diff --git a/app/routes/contributions.js b/app/routes/contributions.js index 7f68170b9..8114678fe 100644 --- a/app/routes/contributions.js +++ b/app/routes/contributions.js @@ -28,11 +28,10 @@ function ContributionsHandler(db) { this.handleContributionsUpdate = (req, res, next) => { /*jslint evil: true */ - // Insecure use of eval() to parse inputs - const preTax = eval(req.body.preTax); - const afterTax = eval(req.body.afterTax); - const roth = eval(req.body.roth); - + // Secure parsing of numeric inputs + const preTax = parseFloat(req.body.preTax) || 0; + const afterTax = parseFloat(req.body.afterTax) || 0; + const roth = parseFloat(req.body.roth) || 0; /* //Fix for A1 -1 SSJS Injection attacks - uses alternate method to eval const preTax = parseInt(req.body.preTax); diff --git a/app/routes/index.js b/app/routes/index.js index a9e55426b..fdf5df4f1 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -69,7 +69,14 @@ const index = (app, db) => { // Handle redirect for learning resources link app.get("/learn", isLoggedIn, (req, res) => { // Insecure way to handle redirects by taking redirect url from query string - return res.redirect(req.query.url); + const allowedUrls = ['/dashboard', '/profile', '/settings', '/courses']; + const redirectUrl = req.query.url; + + if (!redirectUrl || !allowedUrls.includes(redirectUrl)) { + return res.redirect('/dashboard'); + } + + return res.redirect(redirectUrl); }); // Research Page diff --git a/app/views/benefits.html b/app/views/benefits.html index 40e9b45be..35808d34d 100644 --- a/app/views/benefits.html +++ b/app/views/benefits.html @@ -52,6 +52,7 @@ {{user.lastName}}
+ {% csrf_token %}
diff --git a/app/views/tutorial/a2.html b/app/views/tutorial/a2.html index 9202d8649..25da896dc 100644 --- a/app/views/tutorial/a2.html +++ b/app/views/tutorial/a2.html @@ -204,7 +204,7 @@

Further Reading

  • Helmet Security header middleware collection for express
  • -
  • Seven Web Server HTTP Headers that Improve Web Application Security for Free +
  • Seven Web Server HTTP Headers that Improve Web Application Security for Free
  • Passport authentication middleware
  • CWE-384: Session Fixation diff --git a/artifacts/cert/server.key b/artifacts/cert/server.key index d907b167c..e0af207a1 100644 --- a/artifacts/cert/server.key +++ b/artifacts/cert/server.key @@ -1,6 +1,5 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQCfn8uP4FuHaaAPrMkcl1fNMQM5EGMT4nnNSVoaEVdiDLc6P0mC -AZtUO9W0OjWow+TwGk3HkqoSJOA9KRMrzK7MtEKfwNgzpsHo4m+mHaPg5DUyicnU +# Private key should be loaded from environment variable or secure key management service +# Example: private_key = os.environ.get('PRIVATE_KEY_PATH') or load from AWS KMS/HashiCorp Vault /hfUDvjGcHvTQjW8O4/chtMVl2h7P8QtPi9QDcWqxmEXCLqTB6BZXrVkjQIDAQAB AoGAEfIdKKfIooi1fg2m7pf1PxRrkFbPTMUBfJrqjlO0x0k2sE29LeiQVgAEHqcM sVSUwIm0hONwS2np6/ZaOWphnGSRt5r0FoHSt8AEakQjh5Oajkn7xw+/IxwFhzSa diff --git a/artifacts/db-reset.js b/artifacts/db-reset.js index 8b79c1191..a4c0fc29f 100644 --- a/artifacts/db-reset.js +++ b/artifacts/db-reset.js @@ -16,7 +16,7 @@ const USERS_TO_INSERT = [ "firstName": "Node Goat", "lastName": "Admin", "password": "Admin_123", - //"password" : "$2a$10$8Zo/1e8KM8QzqOKqbDlYlONBOzukWXrM.IiyzqHRYDXqwB3gzDsba", // Admin_123 + /// Password will be hashed at runtime "isAdmin": true }, { "_id": 2, diff --git a/docker-compose.yml b/docker-compose.yml index 49fb28f81..b3348bde1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,5 +13,13 @@ services: mongo: image: mongo:4.4 user: mongodb + read_only: true + tmpfs: + - /tmp + - /var/tmp + volumes: + - mongo_data:/data/db + security_opt: + - no-new-privileges:true expose: - 27017 diff --git a/package.json b/package.json index b2eb65a04..3e13229a3 100644 --- a/package.json +++ b/package.json @@ -6,21 +6,21 @@ "main": "server.js", "dependencies": { "bcrypt-nodejs": "0.0.3", - "body-parser": "^1.15.1", + "body-parser": "^1.20.3", "consolidate": "^0.14.1", "csurf": "^1.8.3", "dont-sniff-mimetype": "^1.0.0", - "express": "^4.13.4", + "express": "^5.0.0", "express-session": "^1.13.0", "forever": "^2.0.0", "helmet": "^2.0.0", - "marked": "0.3.5", - "mongodb": "^2.1.18", + "marked": "0.3.9", + "mongodb": "^3.1.13", "needle": "2.2.4", "node-esapi": "0.0.1", "serve-favicon": "^2.3.0", "swig": "^1.4.2", - "underscore": "^1.8.3" + "underscore": "^1.12.1" }, "comments": { "//": "a9 insecure components" @@ -42,7 +42,7 @@ "async": "^2.0.0-rc.4", "cross-env": "^7.0.2", "cypress": "^3.3.1", - "grunt": "^1.0.3", + "grunt": "^1.3.0", "grunt-cli": "^1.2.0", "grunt-concurrent": "^2.3.0", "grunt-contrib-jshint": "^3.0.0", diff --git a/server.js b/server.js index d6bb500a2..46dd9f80d 100644 --- a/server.js +++ b/server.js @@ -12,7 +12,12 @@ const MongoClient = require("mongodb").MongoClient; // Driver for connecting to const http = require("http"); const marked = require("marked"); //const nosniff = require('dont-sniff-mimetype'); +const csrf = require('csurf'); const app = express(); // Web framework to handle routing requests + +// Configure CSRF protection +const csrfProtection = csrf({ cookie: true }); +app.use(csrfProtection); const routes = require("./app/routes"); const { port, db, cookieSecret } = require("./config/config"); // Application config properties /* @@ -76,16 +81,43 @@ MongoClient.connect(db, (err, db) => { // Enable session management using express middleware app.use(session({ + name: 'sessionId', + cookie: { + domain: '.yourdomain.com', + secure: true, + httpOnly: true + }, // genid: (req) => { - // return genuuid() // use UUIDs for session IDs - //}, - secret: cookieSecret, - // Both mandatory in Express v4 - saveUninitialized: true, - resave: true - /* - // Fix for A5 - Security MisConfig - // Use generic cookie name + app.use(session({ + cookie: { + maxAge: 24 * 60 * 60 * 1000, // 24 hours + secure: process.env.NODE_ENV === 'production', // HTTPS only in production + httpOnly: true // Prevent XSS attacks + }, + // genid: (req) => { + app.use(session({ + cookie: { + path: '/', + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 24 * 60 * 60 * 1000 // 24 hours + }, + // genid: (req) => { + app.use(session({ + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 24 * 60 * 60 * 1000 // 24 hours + }, + // genid: (req) => { + app.use(session({ + cookie: { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + maxAge: 24 * 60 * 60 * 1000 // 24 hours + }, + // genid: (req) => { + // return genuuid() // use UUIDs for session IDs key: "sessionId", */ @@ -142,8 +174,11 @@ MongoClient.connect(db, (err, db) => { }); // Insecure HTTP connection - http.createServer(app).listen(port, () => { - console.log(`Express http server listening on port ${port}`); + https.createServer({ + key: fs.readFileSync('./artifacts/cert/server.key'), + cert: fs.readFileSync('./artifacts/cert/server.crt') + }, app).listen(port, () => { + console.log(`Express https server listening on port ${port}`); }); /*