#121 Added CSRF protection

master
Keith Irwin 2018-03-04 20:15:43 +00:00
parent 3af3d9aa96
commit 50061c370c
No known key found for this signature in database
GPG Key ID: 378933C743E2BBC0
9 changed files with 93 additions and 18 deletions

61
package-lock.json generated
View File

@ -1228,6 +1228,16 @@
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
"dev": true "dev": true
}, },
"csrf": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/csrf/-/csrf-3.0.6.tgz",
"integrity": "sha1-thEg3c7q/JHnbtUxO7XAsmZ7cQo=",
"requires": {
"rndm": "1.2.0",
"tsscmp": "1.0.5",
"uid-safe": "2.1.4"
}
},
"css-color-names": { "css-color-names": {
"version": "0.0.4", "version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
@ -1327,6 +1337,34 @@
"source-map": "0.5.7" "source-map": "0.5.7"
} }
}, },
"csurf": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/csurf/-/csurf-1.9.0.tgz",
"integrity": "sha1-SdLGkl/87Ht95VlZfBU/pTM2QTM=",
"requires": {
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"csrf": "3.0.6",
"http-errors": "1.5.1"
},
"dependencies": {
"http-errors": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz",
"integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=",
"requires": {
"inherits": "2.0.3",
"setprototypeof": "1.0.2",
"statuses": "1.4.0"
}
},
"setprototypeof": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz",
"integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg="
}
}
},
"d": { "d": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
@ -6046,6 +6084,11 @@
"resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM="
}, },
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"randomatic": { "randomatic": {
"version": "1.1.7", "version": "1.1.7",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
@ -6425,6 +6468,11 @@
"inherits": "2.0.3" "inherits": "2.0.3"
} }
}, },
"rndm": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz",
"integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w="
},
"run-async": { "run-async": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
@ -7123,6 +7171,11 @@
"punycode": "1.4.1" "punycode": "1.4.1"
} }
}, },
"tsscmp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz",
"integrity": "sha1-fcSjOvcVgatDN9qR2FylQn69mpc="
},
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
@ -7203,6 +7256,14 @@
"resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
"integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE="
}, },
"uid-safe": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.4.tgz",
"integrity": "sha1-Otbzg2jG1MjHXsF2I/t5qh0HHYE=",
"requires": {
"random-bytes": "1.0.0"
}
},
"uid2": { "uid2": {
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",

View File

@ -10,6 +10,7 @@
"cookie-parser": "^1.4.3", "cookie-parser": "^1.4.3",
"cookie-session": "^2.0.0-beta.2", "cookie-session": "^2.0.0-beta.2",
"css-loader": "^0.28.7", "css-loader": "^0.28.7",
"csurf": "^1.9.0",
"debug": "^2.6.9", "debug": "^2.6.9",
"express": "^4.15.5", "express": "^4.15.5",
"express-request-limit": "^1.0.2", "express-request-limit": "^1.0.2",

View File

@ -7,6 +7,7 @@ const rateLimit = require('express-request-limit')
const bodyParser = require('body-parser') const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser') const cookieParser = require('cookie-parser')
const cookieSession = require('cookie-session') const cookieSession = require('cookie-session')
const csurf = require('csurf')
const mongoose = require('mongoose') const mongoose = require('mongoose')
const nunjucks = require('nunjucks') const nunjucks = require('nunjucks')
const passport = require('passport') const passport = require('passport')
@ -49,7 +50,7 @@ let ready_promise_list = []
/* Templates */ { /* Templates */ {
nunjucks.configure(__dirname + '/views', { nunjucks.configure(__dirname + '/views', {
autoescape: true, autoescape: true,
express: app express: app,
}) })
app.set('view engine', 'html') app.set('view engine', 'html')
} }
@ -66,11 +67,11 @@ let ready_promise_list = []
}, },
secret: env.session, secret: env.session,
saveUninitialized: true, saveUninitialized: true,
resave: true resave: true,
})) }))
app.use(bodyParser.json()) app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ app.use(bodyParser.urlencoded({
extended: true extended: true,
})) }))
app.use(flash()) app.use(flash())
} }
@ -149,7 +150,7 @@ let ready_promise_list = []
res.status(err.status || 500) res.status(err.status || 500)
res.render('error', { res.render('error', {
code: err.status || 500, code: err.status || 500,
message: (err.status <= 499) ? err.message : 'Server error' message: (err.status < 500) ? err.message : 'Server error'
}) })
}) })
@ -168,6 +169,11 @@ let ready_promise_list = []
} }
} }
// CSRF Protection
app.use(csurf({
cookie: true,
}))
/* Sockets */ { /* Sockets */ {
sockets.init(io) sockets.init(io)
} }

View File

@ -19,6 +19,7 @@
<h1>Contact</h1> <h1>Contact</h1>
<form id='contact-form' role="form" method="POST"> <form id='contact-form' role="form" method="POST">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input id='subject-input' name="subject" id='subject' type="text" maxlength="160" placeholder="Subject"> <input id='subject-input' name="subject" id='subject' type="text" maxlength="160" placeholder="Subject">
<p id='message-help' class='red help'>You need to enter a message. </p> <p id='message-help' class='red help'>You need to enter a message. </p>

View File

@ -13,6 +13,7 @@
<p>Enter your email below to recieve a link to reset your password. </p> <p>Enter your email below to recieve a link to reset your password. </p>
<form method="post" role="form"> <form method="post" role="form">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<div class='form-group'> <div class='form-group'>
<label for="email">Email:</label> <label for="email">Email:</label>

View File

@ -24,6 +24,7 @@
<h3>Login</h3> <h3>Login</h3>
<form method="post"> <form method="post">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<div id='social-login' class='flex form-group'> <div id='social-login' class='flex form-group'>
@ -64,6 +65,7 @@
<h3>Create account</h3> <h3>Create account</h3>
<p>Welcome aboard! </p> <p>Welcome aboard! </p>
<form action="/signup" method="post"> <form action="/signup" method="post">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<input type="email" name="email" placeholder="Your email" required> <input type="email" name="email" placeholder="Your email" required>
<p>You will be sent an email confrimation with a link to create a password. </p> <p>You will be sent an email confrimation with a link to create a password. </p>
<p>By signing up, you agree to our <a href="/terms">terms of service</a> and <a href="/privacy">privacy policy</a>. </p> <p>By signing up, you agree to our <a href="/terms">terms of service</a> and <a href="/privacy">privacy policy</a>. </p>

View File

@ -18,20 +18,21 @@
<h1>Set Password</h1> <h1>Set Password</h1>
<form id='password-form' role="form" method="post"> <form id='password-form' role="form" method="post">
<style> <input type="hidden" name="_csrf" value="{{csrfToken}}">
#password-form .password { <style>
flex-grow: 1; #password-form .password {
min-width: 0; flex-grow: 1;
margin: 2%; min-width: 0;
} margin: 2%;
#password-help { }
display: none; #password-help {
} display: none;
.form-group > span { }
display: flex; .form-group > span {
flex-wrap: wrap; display: flex;
} flex-wrap: wrap;
</style> }
</style>
<p>Your password must be at least 8 characters long. You can use any letter, number, symbol, emoji, or spaces. Your password will be checked using <a href="https://github.com/dropbox/zxcvbn">zxcvbn</a>. All passwords are stored on the server as salted hashes. </p> <p>Your password must be at least 8 characters long. You can use any letter, number, symbol, emoji, or spaces. Your password will be checked using <a href="https://github.com/dropbox/zxcvbn">zxcvbn</a>. All passwords are stored on the server as salted hashes. </p>

View File

@ -28,6 +28,7 @@
<a href="https://www.keithirwin.us/">Keith Irwin</a></p> <a href="https://www.keithirwin.us/">Keith Irwin</a></p>
<form class='flex' action="#" method="POST"> <form class='flex' action="#" method="POST">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
{% if user.isPro %} {% if user.isPro %}
<div id='already-pro' class='inline-block alert alert-success'> <div id='already-pro' class='inline-block alert alert-success'>
<i class="fa fa-check-circle"></i> <i class="fa fa-check-circle"></i>

View File

@ -19,6 +19,7 @@
<h1>Settings</h1> <h1>Settings</h1>
<form id='settings-form' role="form" method="post"> <form id='settings-form' role="form" method="post">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<h2>Account settings</h2> <h2>Account settings</h2>