Browse Source

Implement CSRF protection

Enabled for all POST endpoints, as well as for GET /rpc-browser when the
"execute" query string argument is specified.
fix-133-memory-crash
Nadav Ivgi 6 years ago
parent
commit
44d676650f
  1. 6
      app.js
  2. 1
      package.json
  3. 39
      routes/baseActionsRouter.js
  4. 1
      views/browser.pug
  5. 2
      views/connect.pug
  6. 2
      views/layout.pug
  7. 1
      views/search.pug
  8. 3
      views/terminal.pug

6
app.js

@ -11,6 +11,7 @@ var logger = require('morgan');
var cookieParser = require('cookie-parser'); var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser'); var bodyParser = require('body-parser');
var session = require("express-session"); var session = require("express-session");
var csurf = require("csurf");
var config = require("./app/config.js"); var config = require("./app/config.js");
var simpleGit = require('simple-git'); var simpleGit = require('simple-git');
var utils = require("./app/utils.js"); var utils = require("./app/utils.js");
@ -454,6 +455,11 @@ app.use(function(req, res, next) {
next(); next();
}); });
app.use(csurf(), (req, res, next) => {
res.locals.csrfToken = req.csrfToken();
next();
});
app.use('/', baseActionsRouter); app.use('/', baseActionsRouter);
/// catch 404 and forwarding to error handler /// catch 404 and forwarding to error handler

1
package.json

@ -28,6 +28,7 @@
"body-parser": "~1.18.2", "body-parser": "~1.18.2",
"cookie-parser": "~1.4.3", "cookie-parser": "~1.4.3",
"crypto-js": "3.1.9-1", "crypto-js": "3.1.9-1",
"csurf": "^1.9.0",
"debug": "~2.6.0", "debug": "~2.6.0",
"decimal.js": "7.2.3", "decimal.js": "7.2.3",
"dotenv": "^6.2.0", "dotenv": "^6.2.0",

39
routes/baseActionsRouter.js

@ -1,4 +1,5 @@
var express = require('express'); var express = require('express');
var csurf = require('csurf');
var router = express.Router(); var router = express.Router();
var util = require('util'); var util = require('util');
var moment = require('moment'); var moment = require('moment');
@ -14,6 +15,8 @@ var coins = require("./../app/coins.js");
var config = require("./../app/config.js"); var config = require("./../app/config.js");
var coreApi = require("./../app/api/coreApi.js"); var coreApi = require("./../app/api/coreApi.js");
const forceCsrf = csurf({ ignoreMethods: [] });
router.get("/", function(req, res) { router.get("/", function(req, res) {
if (req.session.host == null || req.session.host.trim() == "") { if (req.session.host == null || req.session.host.trim() == "") {
if (req.cookies['rpc-host']) { if (req.cookies['rpc-host']) {
@ -815,7 +818,7 @@ router.post("/rpc-terminal", function(req, res) {
}); });
}); });
router.get("/rpc-browser", function(req, res) { router.get("/rpc-browser", function(req, res, next) {
if (!config.demoSite) { if (!config.demoSite) {
var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
var match = config.ipWhitelistForRpcCommands.exec(ip); var match = config.ipWhitelistForRpcCommands.exec(ip);
@ -883,26 +886,30 @@ router.get("/rpc-browser", function(req, res) {
return; return;
} }
console.log("Executing RPC '" + req.query.method + "' with params: [" + argValues + "]"); forceCsrf(req, res, err => {
if (err) return next(err);
console.log("Executing RPC '" + req.query.method + "' with params: [" + argValues + "]");
client.command([{method:req.query.method, parameters:argValues}], function(err3, result3, resHeaders3) {
console.log("RPC Response: err=" + err3 + ", result=" + result3 + ", headers=" + resHeaders3);
if (err3) {
if (result3) {
res.locals.methodResult = {error:("" + err3), result:result3};
client.command([{method:req.query.method, parameters:argValues}], function(err3, result3, resHeaders3) { } else {
console.log("RPC Response: err=" + err3 + ", result=" + result3 + ", headers=" + resHeaders3); res.locals.methodResult = {error:("" + err3)};
}
} else if (result3) {
res.locals.methodResult = result3;
if (err3) {
if (result3) {
res.locals.methodResult = {error:("" + err3), result:result3};
} else { } else {
res.locals.methodResult = {error:("" + err3)}; res.locals.methodResult = {"Error":"No response from node."};
} }
} else if (result3) {
res.locals.methodResult = result3;
} else {
res.locals.methodResult = {"Error":"No response from node."};
}
res.render("browser"); res.render("browser");
});
}); });
} else { } else {
res.render("browser"); res.render("browser");

1
views/browser.pug

@ -46,6 +46,7 @@ block content
hr hr
form(method="get") form(method="get")
input(type="hidden", name="_csrf", value=csrfToken)
input(type="hidden", name="method", value=method) input(type="hidden", name="method", value=method)
div(class="h5 mb-3") Arguments div(class="h5 mb-3") Arguments

2
views/connect.pug

@ -5,6 +5,8 @@ block content
hr hr
form(method="post", action="/connect") form(method="post", action="/connect")
input(type="hidden", name="_csrf", value=csrfToken)
div(class="form-group") div(class="form-group")
label(for="input-host") Host / IP label(for="input-host") Host / IP
input(id="input-host", type="text", name="host", class="form-control", placeholder="Host / IP", value=host) input(id="input-host", type="text", name="host", class="form-control", placeholder="Host / IP", value=host)

2
views/layout.pug

@ -2,6 +2,7 @@ doctype html
html(lang="en") html(lang="en")
head head
meta(charset="utf-8") meta(charset="utf-8")
meta(name="csrf-token", content=csrfToken)
meta(name="viewport", content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, shrink-to-fit=no") meta(name="viewport", content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, shrink-to-fit=no")
if (session.uiTheme && session.uiTheme == "dark") if (session.uiTheme && session.uiTheme == "dark")
@ -86,6 +87,7 @@ html(lang="en")
span Dark span Dark
form(method="post", action="/search", class="form-inline") form(method="post", action="/search", class="form-inline")
input(type="hidden", name="_csrf", value=csrfToken)
div(class="input-group input-group-sm") div(class="input-group input-group-sm")
input(type="text", class="form-control form-control-sm", name="query", placeholder="block height/hash, txid, address", value=(query), style="width: 300px;") input(type="text", class="form-control form-control-sm", name="query", placeholder="block height/hash, txid, address", value=(query), style="width: 300px;")
div(class="input-group-append") div(class="input-group-append")

1
views/search.pug

@ -9,6 +9,7 @@ block content
div(class="mb-5") div(class="mb-5")
form(method="post", action="/search", class="form") form(method="post", action="/search", class="form")
input(type="hidden", name="_csrf", value=csrfToken)
div(class="input-group input-group-lg") div(class="input-group input-group-lg")
input(type="text", class="form-control form-control-sm", name="query", placeholder="block height/hash, txid, address", value=(query), style="width: 300px;") input(type="text", class="form-control form-control-sm", name="query", placeholder="block height/hash, txid, address", value=(query), style="width: 300px;")
div(class="input-group-append") div(class="input-group-append")

3
views/terminal.pug

@ -33,6 +33,8 @@ block content
block endOfBody block endOfBody
script. script.
var csrfToken = $('meta[name=csrf-token]').attr('content');
$(document).ready(function() { $(document).ready(function() {
$("#terminal-form").submit(function(e) { $("#terminal-form").submit(function(e) {
e.preventDefault(); e.preventDefault();
@ -41,6 +43,7 @@ block endOfBody
var postData = {}; var postData = {};
postData.cmd = cmd; postData.cmd = cmd;
postData._csrf = csrfToken;
$.post( $.post(
"/rpc-terminal", "/rpc-terminal",

Loading…
Cancel
Save