From fa3b4432b1620983652f6a0e75f25d8da044c470 Mon Sep 17 00:00:00 2001 From: Christopher McMahon Date: Thu, 8 Sep 2016 14:48:08 -0700 Subject: [PATCH 1/4] Add csrf-jwt module --- samples/universal-react-node/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/universal-react-node/package.json b/samples/universal-react-node/package.json index 2e8a8a41f..b1595f80d 100644 --- a/samples/universal-react-node/package.json +++ b/samples/universal-react-node/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "bluebird": "^2.10.2", + "electrode-csrf-jwt": "^3.0.1", "electrode-router-resolver-engine": "^1.0.0", "electrode-server": "^1.0.0", "electrode-static-paths": "^1.0.0", From 8d86a07c0e79ab458be9bfba9d45adb389c412aa Mon Sep 17 00:00:00 2001 From: Christopher McMahon Date: Thu, 8 Sep 2016 15:25:47 -0700 Subject: [PATCH 2/4] Add csrf plugins + config electrode-csrf-jwt configuration csrf demo endpoints plugin + configuration --- .../universal-react-node/config/default.json | 7 ++++ .../server/plugins/csrf.js | 34 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 samples/universal-react-node/server/plugins/csrf.js diff --git a/samples/universal-react-node/config/default.json b/samples/universal-react-node/config/default.json index 17ca74280..3aa2f4845 100644 --- a/samples/universal-react-node/config/default.json +++ b/samples/universal-react-node/config/default.json @@ -1,5 +1,12 @@ { "plugins": { + "electrode-csrf-jwt": { + "options": { + "secret": "shhhhhh", + "expiresIn": 60 + } + }, + "./server/plugins/csrf": {}, "webapp": { "module": "./server/plugins/webapp", "options": { diff --git a/samples/universal-react-node/server/plugins/csrf.js b/samples/universal-react-node/server/plugins/csrf.js new file mode 100644 index 000000000..a3a60cd51 --- /dev/null +++ b/samples/universal-react-node/server/plugins/csrf.js @@ -0,0 +1,34 @@ +"use strict"; +/*eslint-env es6*/ +var plugin = {}; + +/* + Sample endpoints to demonstrate CSRF protection via the electrode-csrf-jwt module. + Note the endpoints require no special configuration for protection to be enabled. +*/ +plugin.register = function (server, options, next) { + /* a demo GET endpoint which will return a CSRF cookie + header */ + server.route({ + method: "GET", + path: "/1", + handler: function (req, reply) { + reply("valid"); + } + }); + /* a demo POST endpoint which will require a CSRF cookie + header */ + server.route({ + method: "POST", + path: "/2", + handler: function (req, reply) { + reply("valid"); + } + }); + next(); +}; + +plugin.register.attributes = { + name: "CSRFPlugin", + version: "0.0.1" +}; + +module.exports = plugin; From 966e32b1d63e4a620e491eabe1ba6d67541fccc6 Mon Sep 17 00:00:00 2001 From: Christopher McMahon Date: Thu, 8 Sep 2016 15:26:32 -0700 Subject: [PATCH 3/4] Add demo component + route --- .../client/components/csrf.jsx | 84 +++++++++++++++++++ .../universal-react-node/client/routes.jsx | 6 +- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 samples/universal-react-node/client/components/csrf.jsx diff --git a/samples/universal-react-node/client/components/csrf.jsx b/samples/universal-react-node/client/components/csrf.jsx new file mode 100644 index 000000000..e13a9bd67 --- /dev/null +++ b/samples/universal-react-node/client/components/csrf.jsx @@ -0,0 +1,84 @@ +import React from "react"; + +export class CSRF extends React.Component { + + constructor() { + super(); + this.state = { + testResult: "" + }; + this.testValid = this.testValid.bind(this); + this.testInvalid = this.testInvalid.bind(this); + } + + doPost(csrfToken, shouldFail) { + fetch('/2', { + credentials: 'same-origin', + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'x-csrf-jwt': csrfToken + }, + body: JSON.stringify({message: "hello"}) + }) + .then((resp) => { + if (resp.status == "200") { + this.setState({testResult: "POST SUCCEEDED with status " + resp.status}); + } else { + this.setState({testResult: "POST FAILED with status " + resp.status}); + } + + }) + .catch((e) => { + this.setState({testResult: "POST FAILED:" + e.toString()}); + }); + } + + testValid() { + this.setState({testResult: "valid"}); + fetch("/1", {credentials: 'same-origin'}) // eslint-disable-line + .then((resp) => { + if (resp.status == "200") { + const token = resp.headers.get('x-csrf-jwt'); + if (token != '') { + console.log("Got CSRF token OK"); + this.doPost(token, false); + } else { + this.setState({testResult: "Unable to get token from GET request"}); + } + } else { + this.setState({testResult: "GET request returned " + resp.status}); + } + }) + .catch((e) => { + this.setState({testResult: e.toString()}); + }); + } + + testInvalid() { + this.doPost("fake", true); + } + + render() { + const text = this.state.testResult; + return ( +
+

Electrode CSRF-JWT Demo

+

This component demonstrates usage of the electrode-csrf-jwt module. Two endpoints are declared in server/plugins/demo.js:

+
    +
  • a GET endpoint, /1, to which the module automatically adds a csrf token header
  • +
  • a POST endpoint, /2, to which the module automatically ensures the presence of a valid token in the request headers
  • +
+

Two simple tests via AJAX (JavaScript must be enabled) are demonstrated below:

+
    +
  • Test Valid POST using a token retrieved from /1 first (should succeed with a 200 status)
  • +
  • Test Invalid POST using a forged token (should fail with a 400 status)
  • +
+
+ {text} +
+
+ ); + } +} diff --git a/samples/universal-react-node/client/routes.jsx b/samples/universal-react-node/client/routes.jsx index 568897a45..980316b27 100644 --- a/samples/universal-react-node/client/routes.jsx +++ b/samples/universal-react-node/client/routes.jsx @@ -1,7 +1,11 @@ import React from "react"; import { Route, IndexRoute} from "react-router"; import { Home } from "./components/home"; +import { CSRF } from "./components/csrf"; export const routes = ( - + + + + ); From 9c8861950c29084d2d405782cda2f9341b80e702 Mon Sep 17 00:00:00 2001 From: Christopher McMahon Date: Thu, 8 Sep 2016 15:26:45 -0700 Subject: [PATCH 4/4] Add link to CSRF demo component --- samples/universal-react-node/client/components/home.jsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/samples/universal-react-node/client/components/home.jsx b/samples/universal-react-node/client/components/home.jsx index 0f56fddbe..c5a2a1ab0 100644 --- a/samples/universal-react-node/client/components/home.jsx +++ b/samples/universal-react-node/client/components/home.jsx @@ -3,7 +3,13 @@ import React from "react"; export class Home extends React.Component { render() { return ( -

Hello Electrode

+
+

Hello Electrode

+

Demonstration Components

+ +
); } }