Skip to content

Commit

Permalink
code examples using Javascript (#507)
Browse files Browse the repository at this point in the history
* 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
northdpole authored and blabla1337 committed Sep 30, 2019
1 parent 92f0ba9 commit 81e101f
Show file tree
Hide file tree
Showing 23 changed files with 1,023 additions and 0 deletions.
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`.
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

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
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
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
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
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
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

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
Loading

0 comments on commit 81e101f

Please sign in to comment.