diff --git a/app.js b/app.js index 5e2f1d76..71b52bf8 100644 --- a/app.js +++ b/app.js @@ -12,6 +12,8 @@ var bodyParser = require('body-parser'); var cookieParser = require('cookie-parser'); var MongoStore = require('connect-mongo')(session); +var csurf = require('csurf') + var app = express(); app.locals.pretty = true; @@ -46,6 +48,21 @@ app.use(session({ }) ); +app.use(csurf({ })); + +app.use(function(request,response,next){ + app.locals._token = request.csrfToken() + next() +}) + +app.use(function (err, req, res, next) { + if (err.code !== 'EBADCSRFTOKEN') return next(err) + + // handle CSRF token errors here + res.status(403) + res.send('Form tampered with') +}) + require('./app/server/routes')(app); http.createServer(app).listen(app.get('port'), function(){ diff --git a/app/public/js/controllers/homeController.js b/app/public/js/controllers/homeController.js index 90255a8d..5b07b2ac 100644 --- a/app/public/js/controllers/homeController.js +++ b/app/public/js/controllers/homeController.js @@ -10,6 +10,9 @@ function HomeController() // confirm account deletion // $('#account-form-btn1').click(function(){$('.modal-confirm').modal('show')}); +// get csrf_token + var token = document.querySelector('meta[name="csrf-token"]').getAttribute('content') + // handle account deletion // $('.modal-confirm .submit').click(function(){ that.deleteAccount(); }); @@ -20,6 +23,9 @@ function HomeController() $.ajax({ url: '/delete', type: 'POST', + headers: { + 'CSRF-Token': token + }, success: function(data){ that.showLockedAlert('Your account has been deleted.
Redirecting you back to the homepage.'); }, @@ -35,6 +41,9 @@ function HomeController() $.ajax({ url: '/logout', type: 'POST', + headers: { + 'CSRF-Token': token + }, data: {logout : true}, success: function(data){ that.showLockedAlert('You are now logged out.
Redirecting you back to the homepage.'); diff --git a/app/server/modules/account-manager.js b/app/server/modules/account-manager.js index 195d52c0..05a44aba 100644 --- a/app/server/modules/account-manager.js +++ b/app/server/modules/account-manager.js @@ -114,6 +114,17 @@ exports.addNewAccount = function(newData, callback) }); } +function validateEmail(email, _id) { + return new Promise(function(resolve, reject) { + return accounts.find({email: email}).toArray(function(err, records) { + if (err) reject(err); + if (records.length > 1) reject('xerox') + if (records.length === 1 && records[0]._id.toString() !== _id) reject('imposter') + resolve(true); + }); + }); +} + exports.updateAccount = function(newData, callback) { let findOneAndUpdate = function(data){ @@ -123,7 +134,14 @@ exports.updateAccount = function(newData, callback) country : data.country } if (data.pass) o.pass = data.pass; - accounts.findOneAndUpdate({_id:getObjectId(data.id)}, {$set:o}, {returnOriginal : false}, callback); + + validateEmail(data.email, data.id) + .then(function() { + accounts.findOneAndUpdate({_id:getObjectId(data.id)}, {$set:o}, {returnOriginal : false}, callback); + }) + .catch(function() { + callback('email-taken'); + }); } if (newData.pass == ''){ findOneAndUpdate(newData); diff --git a/app/server/routes.js b/app/server/routes.js index 8601d5d8..5724a4b4 100644 --- a/app/server/routes.js +++ b/app/server/routes.js @@ -79,6 +79,9 @@ module.exports = function(app) { country : req.body['country'] }, function(e, o){ if (e){ + if (e === 'email-taken') { + return res.status(400).send('email-taken'); + } res.status(400).send('error-updating-account'); } else{ req.session.user = o.value; diff --git a/app/server/views/account.pug b/app/server/views/account.pug index e15553fc..7f5c1aeb 100644 --- a/app/server/views/account.pug +++ b/app/server/views/account.pug @@ -36,6 +36,8 @@ input(type='hidden', value= user._id)#userId .col-sm-9 input.form-control#pass-tf(type='password', name='pass', value='') + input(type="hidden" name="_csrf" value= _token) + hr .form-buttons button(type='button')#account-form-btn1.btn.btn-outline-dark diff --git a/app/server/views/layout.pug b/app/server/views/layout.pug index 310c2355..ad1cb71b 100644 --- a/app/server/views/layout.pug +++ b/app/server/views/layout.pug @@ -6,10 +6,11 @@ html link(rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous") link(rel='stylesheet', href='/css/style.css') link(rel='stylesheet', href='//cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.1.1/gh-fork-ribbon.min.css') + meta(name="csrf-token" content=_token) body block content script(src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous") script(src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous") script(src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous") script(src="https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.2.2/jquery.form.min.js") - block scripts \ No newline at end of file + block scripts diff --git a/app/server/views/login.pug b/app/server/views/login.pug index 832ae5e6..cbda9740 100644 --- a/app/server/views/login.pug +++ b/app/server/views/login.pug @@ -19,6 +19,9 @@ block content button(type='submit')#btn_sign_in.btn.btn-primary span.fa.fa-lock | Sign In + + input(type="hidden" name="_csrf" value= _token) + hr .btm-links #forgot-password @@ -33,4 +36,4 @@ block scripts script(src='/js/views/login.js') script(src='/js/controllers/loginController.js') script(src='/js/form-validators/loginValidator.js') - script(src='/js/form-validators/emailValidator.js') \ No newline at end of file + script(src='/js/form-validators/emailValidator.js') diff --git a/app/server/views/modals/lost-password.pug b/app/server/views/modals/lost-password.pug index a9e5e023..89bb1aa7 100644 --- a/app/server/views/modals/lost-password.pug +++ b/app/server/views/modals/lost-password.pug @@ -12,6 +12,7 @@ label Please enter the email address associated with your account input.form-control(type="email", id='email-tf', name="email").required .alert.alert-danger.hide + input(type="hidden" name="_csrf" value= _token) .modal-footer button(data-dismiss="modal")#cancel.btn.btn-outline-dark Cancel button(type="submit")#retrieve-password-submit.btn.btn-primary Submit diff --git a/app/server/views/modals/reset-password.pug b/app/server/views/modals/reset-password.pug index 2ae5944c..7ec5750c 100644 --- a/app/server/views/modals/reset-password.pug +++ b/app/server/views/modals/reset-password.pug @@ -12,5 +12,6 @@ label Please enter your new password input.form-control(type="password", name="pass", id='pass-tf').required .alert.alert-danger.hide + input(type="hidden" name="_csrf" value= _token) .modal-footer button(type="submit" form="set-password-form")#set-password-submit.btn.btn-primary Submit diff --git a/package.json b/package.json index 2b61b737..72cbaf22 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "body-parser": "^1.19.0", "connect-mongo": "^3.2.0", "cookie-parser": "^1.4.5", + "csurf": "^1.11.0", "emailjs": "^2.2.0", "express": "^4.17.1", "express-session": "^1.17.1",