Skip to content

Commit

Permalink
Adds Node-HTTP implementation library, adds new built-in body parser …
Browse files Browse the repository at this point in the history
…for Express and Node-HTTP (#47)
  • Loading branch information
amorey authored Jun 10, 2024
1 parent 81defe7 commit 85dd094
Show file tree
Hide file tree
Showing 16 changed files with 908 additions and 126 deletions.
118 changes: 93 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ We hope you enjoy using this software. Contributions and suggestions are welcome
## Features

- Runs on both node and edge runtimes
- Includes integrations for [Next.js](packages/nextjs), [Sveltekit](packages/sveltekit) and [Express](packages/express)
- Includes integrations for [Next.js](packages/nextjs), [Sveltekit](packages/sveltekit), [Express](packages/express) and [Node-HTTP](packages/node-http)
- Includes a low-level API for custom integrations ([see here](packages/core))
- Handles form-urlencoded, multipart/form-data or json-encoded HTTP request bodies
- Gets token from HTTP request header or from request body
Expand All @@ -21,6 +21,7 @@ We hope you enjoy using this software. Contributions and suggestions are welcome
* [Next.js](packages/nextjs)
* [SvelteKit](packages/sveltekit)
* [Express](packages/express)
* [Node-HTTP](packages/node-http)
* [Core API](packages/core)

## Quickstart (Next.js)
Expand Down Expand Up @@ -198,38 +199,18 @@ const csrfMiddleware = createCsrfMiddleware({
const app = express();
const port = 3000;

// add body parsing middleware
app.use(express.urlencoded({ extended: false }));

// add csrf middleware
app.use(csrfMiddleware);

// define handlers
app.get('/', (_, res) => {
res.status(200).json({ success: true });
});

// start server
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
});
```

Now, all HTTP submission requests (e.g. POST, PUT, DELETE, PATCH) will be rejected if they do not include a valid CSRF token. To add the CSRF token to your forms, you can fetch it from the `X-CSRF-Token` HTTP response header server-side or client-side. For example:

```javascript
// app.js
...

// define handlers
app.get('/my-form', (req, res) => {
app.get('/', (req, res) => {
const csrfToken = res.getHeader('X-CSRF-Token') || 'missing';
res.send(`
<!doctype html>
<html>
<body>
<p>CSRF token value: ${csrfToken}</p>
<form action="/my-form" method="post">
<form action="/" method="post">
<legend>Form with CSRF (should succeed):</legend>
<input type="hidden" name="csrf_token" value="${csrfToken}" />
<input type="text" name="input1" />
Expand All @@ -240,13 +221,100 @@ app.get('/my-form', (req, res) => {
`);
});

app.post('/my-form', (req, res) => {
app.post('/', (req, res) => {
res.send('success');
});

...
// start server
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
});
```

With the middleware installed, all HTTP submission requests (e.g. POST, PUT, DELETE, PATCH) will be rejected if they do not include a valid CSRF token.

## Quickstart (Node-HTTP)

First, install Edge-CSRF's Node-HTTP integration library:

```console
npm install @edge-csrf/node-http
# or
pnpm add @edge-csrf/node-http
# or
yarn add @edge-csrf/node-http
```

Next, add the Edge-CSRF CSRF protection function to your request handlers:

```javascript
// server.js

import { createServer } from 'http';

import { createCsrfProtect } from '@edge-csrf/node-http';

// initalize csrf protection middleware
const csrfProtect = createCsrfProtect({
cookie: {
secure: process.env.NODE_ENV === 'production',
},
});

// init server
const server = createServer(async (req, res) => {
// apply csrf protection
try {
await csrfProtect(req, res);
} catch (err) {
if (err instanceof CsrfError) {
res.writeHead(403);
res.end('invalid csrf token');
return;
}
throw err;
}

// add handler
if (req.url === '/') {
if (req.method === 'GET') {
const csrfToken = res.getHeader('X-CSRF-Token') || 'missing';
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<!doctype html>
<html>
<body>
<form action="/" method="post">
<legend>Form with CSRF (should succeed):</legend>
<input type="hidden" name="csrf_token" value="${csrfToken}" />
<input type="text" name="input1" />
<button type="submit">Submit</button>
</form>
</body>
</html>
`);
return;
}

if (req.method === 'POST') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('success');
return;
}
}

res.writeHead(404);
res.end('not found');
});

// start server
server.listen(3000, () => {
console.log('Server is listening on port 3000');
});
```

With the CSRF protection method, all HTTP submission requests (e.g. POST, PUT, DELETE, PATCH) will be rejected if they do not include a valid CSRF token.

## Development

### Get the code
Expand Down
3 changes: 0 additions & 3 deletions examples/express/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ const csrfMiddleware = createCsrfMiddleware({
const app = express();
const port = 3000;

// add body parsing middleware
app.use(express.urlencoded({ extended: false }));

// add csrf middleware
app.use(csrfMiddleware);

Expand Down
2 changes: 1 addition & 1 deletion examples/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"dependencies": {
"@edge-csrf/express": "^2.1.0",
"@edge-csrf/express": "^2.2.0",
"express": "^4.19.2"
}
}
21 changes: 21 additions & 0 deletions examples/node-http/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
This is an [Express](https://expressjs.com) example app.

## Getting Started

First, install dependencies:

```bash
npm install
# or
pnpm install
# or
yarn install
```

Next, run the server:

```bash
node app.js
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
9 changes: 9 additions & 0 deletions examples/node-http/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "edge-csrf-example",
"version": "0.1.0",
"private": true,
"type": "module",
"dependencies": {
"@edge-csrf/node-http": "^2.2.0"
}
}
76 changes: 76 additions & 0 deletions examples/node-http/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { createServer } from 'http';

import { CsrfError, createCsrfProtect } from '@edge-csrf/node-http';

// initalize csrf protection method
const csrfProtect = createCsrfProtect({
cookie: {
secure: process.env.NODE_ENV === 'production',
},
});

// init server
const server = createServer(async (req, res) => {
// apply csrf protection
try {
await csrfProtect(req, res);
} catch (err) {
if (err instanceof CsrfError) {
res.writeHead(403);
res.end('invalid csrf token');
return;
}
throw err;
}

// add handler
if (req.url === '/') {
if (req.method === 'GET') {
const csrfToken = res.getHeader('X-CSRF-Token') || 'missing';
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<!doctype html>
<html>
<body>
<p>CSRF token value: ${csrfToken}</p>
<h2>HTML Form Submission Example:</h2>
<form action="/" method="post">
<legend>Form without CSRF (should fail):</legend>
<input type="text" name="input1" />
<button type="submit">Submit</button>
</form>
<br />
<form action="/" method="post">
<legend>Form with incorrect CSRF (should fail):</legend>
<input type="hidden" name="csrf_token" value="notvalid" />
<input type="text" name="input1" />
<button type="submit">Submit</button>
</form>
<br />
<form action="/" method="post">
<legend>Form with CSRF (should succeed):</legend>
<input type="hidden" name="csrf_token" value="${csrfToken}" />
<input type="text" name="input1" />
<button type="submit">Submit</button>
</form>
</body>
</html>
`);
return;
}

if (req.method === 'POST') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('success');
return;
}
}

res.writeHead(404);
res.end('not found');
});

// start server
server.listen(3000, () => {
console.log('Server is listening on port 3000');
});
6 changes: 0 additions & 6 deletions packages/express/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ const csrfMiddleware = createCsrfMiddleware({
const app = express();
const port = 3000;

// add body parsing middleware
app.use(express.urlencoded({ extended: false }));

// add csrf middleware
app.use(csrfMiddleware);

Expand Down Expand Up @@ -107,9 +104,6 @@ const csrfProtect = createCsrfProtect({
const app = express();
const port = 3000;

// add body parsing middleware
app.use(express.urlencoded({ extended: false }));

// add csrf middleware
app.use(async (req, res, next) => {
try {
Expand Down
Loading

0 comments on commit 85dd094

Please sign in to comment.