-
Notifications
You must be signed in to change notification settings - Fork 308
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
code examples using Javascript (#507)
* Added nodejs/expressjs examples Signed-off-by: northdpole <[email protected]> * changes * Refactor some of the code examples into new structure * Improve nav for XSS * Finish refactoring and cleaning files * Update 10-code_example--Prepared_Statements_SQL--.md replaced ESAPI with parameterized inputs since we're not using esapi * first attempt and ID based auth example * minor fixes * closes #14 * closes issue #16 using passport * closes(?) #11 * closes #10 * Update 21-code_example--Password_forget_and_disallow_old_passwords--.md
- Loading branch information
1 parent
92f0ba9
commit 81e101f
Showing
23 changed files
with
1,023 additions
and
0 deletions.
There are no files selected for viewing
66 changes: 66 additions & 0 deletions
66
skf/markdown/code_examples/nodejs-express/1-code_example--CSRF_Token_csurf--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# CSRF Tokens | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
|
||
If you're using JSON over REST to mutate server state and the application doesn't support plain HTML form submissions and your CORS configuration bans cross-domain requests then Express has built-in CSRF protection. | ||
|
||
If you support plain HTML form submissions, read on. | ||
|
||
**Hint:** you can check if you support plain HTML form submissions by searching for: | ||
|
||
```js | ||
const bodyParser = require('body-parser'); | ||
bodyParser.urlencoded(); | ||
``` | ||
|
||
## Example | ||
|
||
The following handlebar template snippet shows the code used to place the antiCSRF token inside a html page. | ||
|
||
When the page renders, the `<cu:antiCSRF/>` is created as a viewstate encoded html input tag which then carries the antiCSRF token. While in process of rendering the page, a new token is generated and added into the existing session. | ||
|
||
When the user presses the commandButton then CSRF token parameter is compared with the CSRF session parameter. | ||
|
||
```hbs | ||
<form action="/process" method="POST"> | ||
<input type="hidden" name="_csrf" value="{{csrfToken}}"> | ||
... | ||
<button type="submit">Submit</button> | ||
</form> | ||
``` | ||
|
||
The following snippet is used to generate and check the token: | ||
|
||
```js | ||
const csrf = require('csurf'); //csrf module | ||
const csrfProtection = csrf({ cookie: true }); // setup route middlewares | ||
|
||
// This is required because "cookie" is true in csrfProtection | ||
app.use(cookieParser()); | ||
|
||
// Error handler(Optional) shows custom error message when token is missing or mismatches | ||
app.use((err, req, res, next) => { | ||
// on token validation fail, error is thrown with code 'CSRFERROR' | ||
if (err.code !== 'CSRFERROR') return next(err); | ||
res.status(403); | ||
res.send('csrf error'); | ||
}); | ||
|
||
// We need to pass the middleware to each route | ||
app.get('/form', csrfProtection, (req, res) => { | ||
// generate and pass the csrfToken to the view | ||
res.render('send', { csrfToken: req.csrfToken() }); | ||
}); | ||
|
||
// and check it when the request is being processed | ||
app.post('/process', parseForm, csrfProtection, (req, res) => { | ||
res.send('data is being processed'); | ||
}); | ||
``` | ||
|
||
## Considerations | ||
`csurf` doesn't protect by default requests such as `GET`, `OPTIONS`, `HEAD`. |
24 changes: 24 additions & 0 deletions
24
...down/code_examples/nodejs-express/10-code_example--Prepared_Statements_SQL--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Encoder (SQL - Parameterized Inputs) | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
TBA | ||
|
||
## Example | ||
Execute prepared statement with parameterized user inputs using [`mysql` module](https://www.npmjs.com/package/mysql): | ||
```js | ||
const sqlQuery = 'SELECT * FROM accounts WHERE username=? AND password=?'; | ||
|
||
connection.query(sqlQuery, [username, passwordHash], (err, rows, fields) => { | ||
// handle both success and failure for query result | ||
}); | ||
|
||
connection.end(); | ||
``` | ||
|
||
## Considerations | ||
TBA | ||
|
14 changes: 14 additions & 0 deletions
14
skf/markdown/code_examples/nodejs-express/12-code_example--File_uploading--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# File Uploading | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
TBA | ||
|
||
## Example | ||
TBA | ||
|
||
## Considerations | ||
TBA |
26 changes: 26 additions & 0 deletions
26
skf/markdown/code_examples/nodejs-express/15-code_example--HttpOnly_flag--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# `httpOnly` flag | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
`httpOnly` flag can be added to the `Set-Cookie` response header in order to dissalow client-side scripts from accessing or modifying the cookie in question. This can help to mitigate most common XSS attacks by protecting the cookie data. | ||
|
||
## Example | ||
When setting sessions with [`express-session` module](https://www.npmjs.com/package/express-session) you can add the `cookie` portion of the configuration as shown below in order to protect session ID cookie: | ||
```js | ||
const session = require('express-session'); | ||
|
||
app.use(session({ | ||
secret: 'some random and long value', | ||
key: 'sessionId', | ||
cookie: { | ||
httpOnly: true, | ||
secure: true | ||
} | ||
})); | ||
``` | ||
|
||
## Considerations | ||
TBA |
84 changes: 84 additions & 0 deletions
84
...de_examples/nodejs-express/17-code_example--Identifier_based_authorization--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# Identifier-based authorization | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
Database expected is MS SQL server making use of [mssql](https://www.npmjs.com/package/mssql). | ||
`file_access` is formatted as so: | ||
| user_id | file_id | | ||
|---------|---------| | ||
| 1 | 2 | | ||
Where both ids are foreign keys and the primary key is made of a composite of the two. | ||
|
||
## Example | ||
```js | ||
const express = require('express'); | ||
const session = require('express-session') | ||
const FileStore = require('session-file-store')(session); | ||
const bodyParser = require('body-parser'); | ||
const passport = require('passport'); | ||
const LocalStrategy = require('passport-local').Strategy; | ||
const sql = require('mssql') | ||
|
||
//Made up external files | ||
const validator = require('./validator'); //Handles validating logins | ||
const files = require('./files'); //Gets files from DB or store | ||
|
||
|
||
|
||
// configure passport.js to use the local strategy | ||
passport.use(new LocalStrategy( | ||
{ usernameField: 'email' }, (email, password, done) => { | ||
const user = validator.login(email, password); //Validator returns false if invalid | ||
return done(null, user) | ||
} | ||
)); | ||
|
||
// tell passport how to serialize the user | ||
passport.serializeUser((user, done) => { | ||
done(null, user.id); | ||
}); | ||
|
||
passport.deserializeUser((id, done) => { | ||
const user = users.getUserById(id); | ||
done(null, user); | ||
}); | ||
|
||
// create the server | ||
const app = express(); | ||
|
||
// add & configure middleware | ||
app.use(bodyParser.urlencoded({ extended: false })) | ||
app.use(bodyParser.json()) | ||
app.use(session({ | ||
store: new FileStore(), | ||
secret: process.env.sessionKey, //always use environment variables to pass in keys. | ||
resave: false, | ||
saveUninitialized: true | ||
})) | ||
app.use(passport.initialize()); | ||
app.use(passport.session()); | ||
|
||
//Login excluded | ||
|
||
app.get('/post', passport.authenticate('local', { failureRedirect: '/login' }), //authenticates the user using session | ||
async function(req, res) { | ||
const data = req.body; | ||
let pool = await sql.connect(config) | ||
let result = await pool.request() | ||
.input('user_id', sql.Int, req.user.id) //sql.Int validates that only a integer value can be in the variable | ||
.input('file_id', sql.Int, data.id) | ||
.query('select * from file_access where user_id = @user_id and file_id = @file_id'); //variables inlined into sql query | ||
|
||
if(result.recordsets.length === 1) { //If the result exists the user has access | ||
res.send(files.getFile(data.id)) //sends file | ||
} else { | ||
res.redirect('/invalidFile'); //redirects to a generic invalid file | ||
} | ||
}); | ||
``` | ||
|
||
## Considerations | ||
TBA |
46 changes: 46 additions & 0 deletions
46
skf/markdown/code_examples/nodejs-express/18-code_example--Login_function--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Login Functionality | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
TBA | ||
|
||
## Example | ||
Using the [Passport middleware](http://www.passportjs.org/) | ||
|
||
The following example assumes username/password authentication. | ||
|
||
First, configure the middleware: | ||
``` | ||
var auth_manager = require('passport') | ||
, LocalStrategy = require('passport-local').Strategy; | ||
auth_manager.use(new LocalStrategy( | ||
function(username, password, done) { | ||
User.findOne({ username: username }, function(err, user) { | ||
if (err) { return done(err); } | ||
if (!user) { | ||
return done(null, false, { message: 'Incorrect username.' }); | ||
} | ||
if (!user.validPassword(password)) { | ||
return done(null, false, { message: 'Incorrect password.' }); | ||
} | ||
return done(null, user); | ||
}); | ||
} | ||
)); | ||
``` | ||
|
||
Then, register the route handling authentication can be: | ||
``` | ||
app.post('/login', | ||
auth_manager.authenticate('local', { successRedirect: '/', | ||
failureRedirect: '/login' | ||
}) | ||
); | ||
``` | ||
|
||
## Considerations | ||
TBA |
21 changes: 21 additions & 0 deletions
21
skf/markdown/code_examples/nodejs-express/19-code_example--Logout--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Logout Functionality | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
|
||
## Example | ||
Using [Passport](http://www.passportjs.org/docs/logout/) as a middleware call logOut() or logout() on your req object. | ||
``` | ||
app.get('/logout', function(req, res){ | ||
req.logout(); | ||
res.redirect('/'); | ||
}); | ||
``` | ||
|
||
|
||
## Considerations | ||
TBA |
23 changes: 23 additions & 0 deletions
23
skf/markdown/code_examples/nodejs-express/2-code_example--Charsets--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Charsets | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
TBA | ||
|
||
## Example | ||
Charset header should be set on the response your server sends back to the client. For example, in the case of `text/html` this can be achieved by the following code: | ||
```js | ||
res.charset = 'utf-8'; //utf-8 is the default encoding for json | ||
``` | ||
|
||
Or directly in your HTML markup: | ||
```html | ||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | ||
``` | ||
|
||
### Considerations | ||
TBA | ||
|
35 changes: 35 additions & 0 deletions
35
.../code_examples/nodejs-express/20-code_example--Open_Forwards_and_redirects--.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Open Forwards and Redirects | ||
|
||
- [General](#general) | ||
- [Example](#example) | ||
- [Considerations](#considerations) | ||
|
||
## General | ||
TBA | ||
|
||
## Example | ||
When using forwards and redirects you should make sure the URL is being explicitly declared in the code and cannot be manipulated by an attacker like in the case of `redirectTo` being dynamically set based on user input: | ||
```js | ||
app.get('/offers', (req, res, next) => { | ||
const redirectTo = req.query.redirect; | ||
res.redirect(redirectTo); | ||
}); | ||
``` | ||
|
||
Generally you should avoid getting parameters which could contain user input into the redirect by any means. If for any reason this is not feasible, then you should make a whitelist input validation for the redirect as shown below: | ||
```js | ||
const validRedirectURLs = [...]; // list of URLs permitted for redirection | ||
|
||
app.get('/offers', (req, res, next) => { | ||
const redirectTo = req.query.redirect; | ||
|
||
if(validRedirectURLs.includes(redirectTo)) { | ||
res.redirect(redirectTo); | ||
} else { | ||
return res.status(500).send({ error: 'Invalid redirection URL' }); | ||
} | ||
}); | ||
``` | ||
|
||
## Considerations | ||
TBA |
Oops, something went wrong.