Comments (6)
You could try putting your /report-violation
route before the CSRF middleware. Could you post your middleware setup (or some of it)?
from helmet.
That might work - however today I load all my routes via external "controllers" - I'd have to break out a special route - but it could work.
Here's my full express app:
'use strict';
/**
* Module Dependencies
*/
// Express 4.x Modules
var csrf = require('csurf'); // https://github.com/expressjs/csurf
var logger = require('morgan'); // https://github.com/expressjs/morgan
var express = require('express'); // https://npmjs.org/package/express
var favicon = require('serve-favicon'); // https://github.com/expressjs/favicon
var session = require('express-session'); // https://github.com/expressjs/session
var compress = require('compression'); // https://github.com/expressjs/compression
var bodyParser = require('body-parser'); // https://github.com/expressjs/body-parser
var serveStatic = require('serve-static'); // https://github.com/expressjs/serve-static
var errorHandler = require('errorhandler'); // https://github.com/expressjs/errorhandler
var methodOverride = require('method-override'); // https://github.com/expressjs/method-override
// Additional Modules
var fs = require('fs'); // http://nodejs.org/docs/v0.10.25/api/fs.html
var path = require('path'); // http://nodejs.org/docs/v0.10.25/api/path.html
var debug = require('debug')('skeleton'); // https://github.com/visionmedia/debug
var flash = require('express-flash'); // https://npmjs.org/package/express-flash
var config = require('./config/config'); // Get configuration file
var helmet = require('helmet'); // https://github.com/evilpacket/helmet
var semver = require('semver'); // https://npmjs.org/package/semver
var enforce = require('express-sslify'); // https://github.com/florianheinemann/express-sslify
var winston = require('winston'); // https://npmjs.org/package/winston
var mongoose = require('mongoose'); // https://npmjs.org/package/mongoose
var passport = require('passport'); // https://npmjs.org/package/passport
var MongoStore = require('connect-mongo')(session); // https://npmjs.org/package/connect-mongo
var expressValidator = require('express-validator'); // https://npmjs.org/package/express-validator
/**
* Create Express App
*/
var app = module.exports = express(); // export app for testing ;)
/**
* Create Express HTTP Server and socket.io listener
*/
var server = require('http').Server(app);
var io = require('socket.io')(server);
/**
* Configure Logging
*/
// TODO: Logging in production should be directed to a logging service
// such as loggly.com or to a log server or database.
if (config.logging) {
winston.add(winston.transports.File, { filename: config.logfilename });
}
// Turn off Winston console logging, we will use Express instead
winston.remove(winston.transports.Console);
/**
* Configure Mongo Database
*/
mongoose.connect(config.mongodb.url);
var db = mongoose.connection;
// Use Mongo for session store
config.session.store = new MongoStore({
mongoose_connection: db,
auto_reconnect: true
});
/**
* Express Configuration and Setup
*/
// Application local variables are provided to *all* templates
// rendered within the application. (personally I would have called
// them "app.globals") They are useful for providing helper
// functions to templates, as well as global app-level data.
// NOTE: you must *not* reuse existing (native) named properties
// for your own variable names, such as name, apply, bind, call,
// arguments, length, and constructor.
app.locals.application = config.name;
app.locals.version = config.version;
app.locals.description = config.description;
app.locals.author = config.author;
app.locals.keywords = config.keywords;
app.locals.ga = config.ga;
// Stuff moment.js into app.locals then use
// moment anywhere within a jade template like this:
// p #{moment(Date.now()).format('MM/DD/YYYY')}
// Good for an evergreen copyright ;)
app.locals.moment = require('moment');
if (app.get('env') === 'development') {
// Jade options: Don't minify html, debug intrumentation
app.locals.pretty = true;
app.locals.compileDebug = true;
// Turn on console logging in development
app.use(logger('dev'));
// Turn off caching in development
// This sets the Cache-Control HTTP header to no-store, no-cache,
// which tells browsers not to cache anything.
app.use(helmet.nocache());
}
if (app.get('env') === 'production') {
// Jade options: minify html, no debug intrumentation
app.locals.pretty = false;
app.locals.compileDebug = false;
// Stream Express Logging to Winston
app.use(logger({
stream: {
write: function (message, encoding) {
winston.info(message);
}
}
}));
// Enable If behind nginx, proxy, or a load balancer (e.g. Heroku, Nodejitsu)
app.enable('trust proxy', 1); // trust first proxy
// Since our application has signup, login, etc. forms these should be protected
// with SSL encryption. Heroku, Nodejitsu and other hosters often use reverse
// proxies or load balancers which offer SSL endpoints (but then forward unencrypted
// HTTP traffic to the server). This makes it simpler for us since we don't have to
// setup HTTPS in express. When in production we can redirect all traffic to SSL
// by using a little middleware.
//
// In case of a non-encrypted HTTP request, enforce.HTTPS() automatically
// redirects to an HTTPS address using a 301 permanent redirect. BE VERY
// CAREFUL with this! 301 redirects are cached by browsers and should be
// considered permanent.
//
// NOTE: Use `enforce.HTTPS(true)` if you are behind a proxy or load
// balancer that terminates SSL for you (e.g. Heroku, Nodejitsu).
app.use(enforce.HTTPS(true));
// This tells browsers, "hey, only use HTTPS for the next period of time".
// This will set the Strict Transport Security header, telling browsers to
// visit by HTTPS for the next ninety days:
// TODO: should we actually have this *and* app.use(enforce.HTTPS(true)); above?
// this seems more flexible rather than a hard redirect.
var ninetyDaysInMilliseconds = 7776000000;
app.use(helmet.hsts({ maxAge: ninetyDaysInMilliseconds }));
// Turn on HTTPS/SSL cookies
config.session.proxy = true;
config.session.cookie.secure = true;
}
// Port to listen on.
app.set('port', config.port);
// Favicon - This middleware will come very early in your stack
// (maybe even first) to avoid processing any other middleware
// if we already know the request is for favicon.ico
app.use(favicon(__dirname + '/public/favicon.ico'));
// Setup the view engine (jade)
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// Compress response data with gzip / deflate.
// This middleware should be placed "high" within
// the stack to ensure all responses are compressed.
app.use(compress());
// http://en.wikipedia.org/wiki/HTTP_ETag
// Google has a nice article about "strong" and "weak" caching.
// It's worth a quick read if you don't know what that means.
// https://developers.google.com/speed/docs/best-practices/caching
app.set('etag', true); // other values 'weak', 'strong'
// Body parsing middleware supporting
// JSON, urlencoded, and multipart requests.
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// Easy form validation!
app.use(expressValidator());
// If you want to simulate DELETE and PUT
// in your app you need methodOverride.
app.use(methodOverride());
// Use sessions
// NOTE: cookie-parser not needed with express-session > v1.5
app.use(session(config.session));
// Security Settings
app.disable('x-powered-by'); // Don't advertise our server type
app.use(csrf()); // Prevent Cross-Site Request Forgery
app.use(helmet.nosniff()); // Sets X-Content-Type-Options to nosniff
app.use(helmet.ienoopen()); // X-Download-Options for IE8+
app.use(helmet.xssFilter()); // sets the X-XSS-Protection header
app.use(helmet.xframe('deny')); // Prevent iframe
app.use(helmet.crossdomain()); // crossdomain.xml
// Content Security Policy
// http://content-security-policy.com/
// http://www.html5rocks.com/en/tutorials/security/content-security-policy/
// http://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/
app.use(helmet.csp({
defaultSrc: [
"'self'"
],
scriptSrc: [
"'self'",
"'unsafe-eval'",
"'unsafe-inline'",
'ajax.googleapis.com',
'www.google-analytics.com',
'oss.maxcdn.com',
'cdn.socket.io',
'checkout.stripe.com'
],
styleSrc: [
"'self'",
"'unsafe-inline'",
'fonts.googleapis.com',
'checkout.stripe.com'
],
fontSrc: [
"'self'",
'fonts.googleapis.com',
'themes.googleusercontent.com'
],
imgSrc: [
"'self'",
'data:',
'gravatar.com',
'http://pbs.twimg.com',
'*.4sqi.net',
'avatars.githubusercontent.com',
'http://*.media.tumblr.com',
'http://userserve-ak.last.fm',
'graph.facebook.com',
'*.fbcdn.net',
'fbcdn-profile-a.akamaihd.net',
'github.global.ssl.fastly.net',
'chart.googleapis.com',
'www.google-analytics.com'
],
mediaSrc: [
"'self'"
],
connectSrc: [ // limit the origins (via XHR, WebSockets, and EventSource)
"'self'",
'ws://localhost:3000',
'wss://skeleton-app.jit.su',
'api.github.com'
],
objectSrc: [ // allows control over Flash and other plugins
"'none'"
],
frameSrc: [ // origins that can be embedded as frames
'checkout.stripe.com'
],
sandbox: [
'allow-same-origin',
'allow-forms',
'allow-scripts'
],
reportOnly: false, // set to true if you *only* want to report errors
setAllHeaders: false // set to true if you want to set all headers
}));
// Passport OAUTH Middleware
app.use(passport.initialize());
app.use(passport.session());
// Keep user, csrf token and config available
app.use(function (req, res, next) {
res.locals.user = req.user;
res.locals.config = config;
res.locals._csrf = req.csrfToken();
next();
});
// Flash messages
app.use(flash());
/**
* Routes/Routing
*/
// Dynamically include routes (via controllers)
fs.readdirSync('./controllers').forEach(function (file) {
if (file.substr(-3) === '.js') {
var route = require('./controllers/' + file);
route.controller(app);
}
});
// Now setup serving static assets from /public
// time in milliseconds...
var minute = 1000 * 60; // 60000
var hour = (minute * 60); // 3600000
var day = (hour * 24); // 86400000
var week = (day * 7); // 604800000
app.use(serveStatic(__dirname + '/public', { maxAge: week }));
/**
* Error Handling
*/
// If nothing responded above we will assume a 404 (no routes responded or static assets found)
// Tests:
// $ curl http://localhost:3000/notfound
// $ curl http://localhost:3000/notfound -H "Accept: application/json"
// $ curl http://localhost:3000/notfound -H "Accept: text/plain"
// Handle 404 Errors
app.use(function (req, res, next) {
winston.warn('404 Warning. URL: ' + req.url + '\n');
res.status(404);
// Respond with html page
if (req.accepts('html')) {
res.render('error/404', {
url: req.url
});
return;
}
// Respond with json
if (req.accepts('json')) {
res.send({ error: 'Not found' });
return;
}
// Default to plain-text. send()
res.type('txt').send('Error: Not found');
});
// True error-handling middleware requires an arity of 4,
// aka the signature (err, req, res, next).
// Handle 403 Errors
app.use(function (err, req, res, next) {
if (err.status === 403) {
winston.error('403 Not Allowed. ' + err + '\n');
// Respond with HTML
if (req.accepts('html')) {
res.status(err.status);
res.render('error/403', {
error: err,
url: req.url
});
return;
}
// Respond with json
if (req.accepts('json')) {
res.send({ error: 'Not Allowed!' });
return;
}
// Default to plain-text. send()
res.type('txt').send('Error: Not Allowed!');
} else {
// Since the error is not a 403 pass it along
return next(err);
}
});
// Production 500 error handler (no stacktraces leaked to public!)
if (app.get('env') === 'production') {
app.use(function (err, req, res, next) {
winston.error(err.status || 500 + ' ' + err + '\n');
res.status(err.status || 500);
res.render('error/500', {
error: {}
});
});
}
// Development 500 error handler
if (app.get('env') === 'development') {
app.use(function (err, req, res, next) {
winston.error(err.status || 500 + ' ' + err + '\n');
res.status(err.status || 500);
res.render('error/500', {
error: err
});
});
// Final error catch-all just in case...
app.use(errorHandler({ dumpExceptions: true, showStack: true }));
}
/**
* Start Express server.
*/
// NOTE: To alter the environment we can set the
// NODE_ENV environment variable, for example:
// $ NODE_ENV=production node app.js
// This is *very* important, as many caching mechanisms
// are *only* enabled when in production!
db.on('error', function () {
winston.error('Mongodb connection error!');
console.error('✗ MongoDB Connection Error. Please make sure MongoDB is running.'.red.bold);
// testing debug functionality
debug('✗ MongoDB Connection Error. Please make sure MongoDB is running.'.red.bold);
process.exit(0);
});
db.on('open', function () {
winston.info('Mongodb connected!');
console.log('✔ Mongodb ' + 'connected!'.green.bold);
// "server.listen" for socket.io
server.listen(app.get('port'), function () {
// Test for correct node version as spec'ed in package.info
if (!semver.satisfies(process.versions.node, config.nodeVersion)) {
winston.error(config.name + ' needs Node version ' + config.nodeVersion);
console.error(
'\nERROR: Unsupported version of Node!'.red.bold,
'\n✗ '.red.bold + config.name.red.bold + ' needs Node version'.red.bold,
config.nodeVersion.yellow.bold,
'you are using version'.red.bold,
process.versions.node.yellow.bold,
'\n✔ Please go to http://nodejs.org to get a supported version.'.red.bold
);
process.exit(0);
}
// Log how we are running
winston.info(config.name + ' listening on port ' + app.get('port'),
'in ' + app.settings.env + ' mode.'
);
console.log(
'✔ ' + config.name + ' listening on port ' + app.get('port').toString().green.bold,
'in ' + app.settings.env.green.bold + ' mode.',
'\n✔ Hint: ' + 'Ctrl+C'.green.bold + ' to shut down.'
);
// Exit cleanly on Ctrl+C
process.on('SIGINT', function () {
winston.info(config.name + ' has shudown.');
console.log(
'\n✔ ' + config.name + ' has ' + 'shutdown'.green.bold,
'\n✔ ' + config.name + ' was running for ' + Math.round(process.uptime()).toString().green.bold + ' seconds.'
);
process.exit(0);
});
});
});
from helmet.
Is your /report-violation
handler in a controller? It also looks like reportUri
isn't set in your CSP middleware.
from helmet.
I just pulled out /report-violation
because it caused a /403 every time. So yep, its missing...
from helmet.
Unfortunately, it looks like csurf
will cause a 403 if (1) it doesn't get a CSRF token with the request (2) it's a request other than a GET, HEAD, or OPTIONS. My solution was to reorder the report-violation handler before CSRF stuff:
app.post('/report-violation', function(req, res) {
/* ... */
});
app.use(csrf());
It doesn't look like you can do this with the current state of csurf
. They seem to be looking into it, though. Stay tuned about this!
Worked this out in a Gist, if that interests you.
I'll add a note about this to the readme and close this for now.
from helmet.
So putting the route above the csurf middleware works - nice. Thanks for the gist! ;)
from helmet.
Related Issues (20)
- Jest: Cannot find module 'helmet' or its corresponding type declarations HOT 3
- Consider limiting helmet to document requests or add a note HOT 4
- Deployment on Vercel using .mjs HOT 13
- X-Powered-By is not being removed from the haeder in default mode HOT 2
- Getting Error Type 'typeof import("/home/quophyie/projects/helmet-issue/node_modules/helmet/index")' has no call signatures when running tests with jest, ts-jest when using ESM / ECMAScript Modules HOT 13
- helmet + sanitizeFilter HOT 1
- Require Node 18+ HOT 5
- Support `unsafe-none` in `helmet.crossOriginEmbedderPolicy`? HOT 1
- Typescript required versions HOT 2
- 7.1.0 Rollup error HOT 17
- Disable HSTS headers by default on localhost HOT 9
- Error "script-src-elem" is an invalid directive HOT 3
- 'self' and 'none' values lack quotes HOT 4
- swagger HOT 2
- Getting Name of Blocked Script? HOT 3
- Increase default Strict-Transport-Security maxAge to 1 year HOT 3
- remove block-all-mixed-content from helmet-csp default directives HOT 6
- helmet default directives doesnt match helmet-csp default directives HOT 3
- Strict-Transport-Security middleware should throw, not warn, when misspelling "includeSubDomains" option HOT 2
- Content-Security-Policy `getDefaultDirectives` should return a deep copy HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from helmet.