Node JS Security

Use Helmet

  1. Helmet can help protect your app from some well-known web vulnerabilities by setting HTTP headers appropriately.
  2. Helmet is actually just a collection of nine smaller middleware functions that set security-related HTTP headers:
  3. csp sets the Content-Security-Policy header to help prevent cross-site scripting attacks and other cross-site injections.
  4. hidePoweredBy removes the X-Powered-By header.
  5. noCache sets Cache-Control and Pragma headers to disable client-side caching.
  6. noSniff sets X-Content-Type-Options to prevent browsers from MIME-sniffing a response away from the declared content-type.
  7. frameguard sets the X-Frame-Options header to provide clickjacking protection.
  8. xssFilter sets X-XSS-Protection to enable the Cross-site scripting (XSS) filter in most recent web browsers.
var csrf = require('csurf');
/*some code here*/
var app = express();
/*some code here*/
app.use(csrf());

app.get('/', function(req, res, next){
  res.render('index', {
    _csrfToken = req.csrfToken();
  });
});

Prevent XSS ! Context Based Encoding

var csrf = require('csurf');
/*some code here*/
var app = express();
/*some code here*/
app.use(csrf());

app.get('/', function(req, res, next){
  res.render('index', {
    _csrfToken = req.csrfToken();
  });
});

Prevent CSRF (Cross Site Request Forgery) with Anti-Forgery Tokens

/*some code here*/

app.use(csrf());
app.use(function (req, res, next) {
    res.cookie('XSRF-TOKEN', req.csrfToken());
    next();
});

Send CSRF Token

/*some code here*/
var express = require('express');
var session = require('express-session');
var app = express();

app.use(session({
    name: 'SESS_ID',
    secret: '^#$5sX(Hf6KUo!#65^',
    resave: false,
    saveUninitialized: true,
    cookie: {
        secure: true,
        httpOnly: true
    }
}));

Secure Express.js Sessions and Cookies

Make cookies more secure

Make cookies more secure

/* some code */

app.get('/', function(req, res, next){
  /*some logic here before setting cookie*/
  res.cookie('User', 'John Doe',
 { httpOnly: true, secure: true });
  res.status(200)
 .json('this is a repsonse with a safe cookie');
});

Signing Cookies

var cookieParser = require('cookie-parser');
var express = require('express');
var app = express();

app.use(cookieParser('6xH$*CYY*u44gcUN57%H'));

app.get('/', function(req, res, next){
  res.cookie('User', 'John Doe', { signed: true });
  res.render('index');
});

app.get('/somepath', function(req, res, next)){
  var currentUser = req.signedCookies['User']; 
// "John Doe" will be returned to 
// currentUser only if the signation is verified
}

Query param population

http://example.com/?firstname='Jack'&lastname='Sparrow'

An attacker would try to attack the application by modifying the url to the following.

http://example.com/?firstname='Jack'&lastname='Sparrow'&firstname='Jill'

Preventing HTTP Parameter Pollution (HPP)

var hpp = require('hpp');
var express = require('express');
var bodyParser = require('body-parser');
var app = express();

app.use(bodyParser.urlencoded({extended: false}));
 //body parser should be placed before using hpp
app.use(hpp());

app.get('/', function(req, res, next){
  console.log('Query Parameters : ' + JSON.stringify(req.query));
  console.log('Polluted Query Parameters : '+ JSON.stringify(req.queryPolluted));
  res.render('index');
});

Setting Security Response Headers

var express = require('express');
var app = express();

app.disable('x-powered-by');   // disable X-Powered-By header

app.use(function(req, res, next){
  res.header('X-XSS-Protection', '1; mode=block');
  res.header('X-Frame-Options', 'deny');
  res.header('X-Content-Type-Options', 'nosniff');
  next();
});

Setting Security Response Headers

  • X-Frame-Options — Prevents your application being displayed in iframes
  • X-XSS-Protection — Invokes browser XSS protection mechanisms
  • X-Content-Type-Options — Prevents mime sniffing

Setting Security Response Headers

var helmet = require('helmet');

app.use(helmet.hidePoweredBy({setTo: 'DummyServer 1.0'})); 
//change value of X-Powered-By header to given value
app.use(helmet.noCache({noEtag: true})); 
//set Cache-Control header
app.use(helmet.noSniff());    
// set X-Content-Type-Options header
app.use(helmet.frameguard()); 
// set X-Frame-Options header
app.use(helmet.xssFilter());  
// set X-XSS-Protection header

NPM Modules

  • csurf — Implement Anti-CSRF tokens to prevent cross site request forgery
  • cors — Enable Cross origin resource sharing
  • hpp — Protection from HTTP Parameter Pollution
  • express-content-length-validator — Prevent DOS attacks
  • rate-limiter — Prevent DOS attacks
  • helmet — Set custom security headers
  • nsp — Scan for deprecated/vulnerable npm modules used in your app
  • retire — Scan for deprecated/vulnerable npm modules used in your app
  • mocha, should, supertest — Writing node.js tests
  • bunyan — Logging

Use explicit package versions

{
  "name": "My App",
  "version": "0.0.0",
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.13.2",
    "cookie-parser": "~1.3.5",
    "debug": "~2.2.0",
    "express": "~4.13.1",
    "http-auth": "~2.2.8",
    "jade": "~1.11.0",
    "jsonfile": "~2.2.1",
    "morgan": "~1.6.1",
    "nodemon": "~1.4.1",
    "serve-favicon": "~2.3.0"
  }
}

No sensitive data inside your repo !!!

app.use(session({
    name: 'SESS_ID',
    secret: process.env.EXPRESS_SESSION_SECRET,
    resave: false,
    saveUninitialized: true,
    cookie: {
        secure: true,
        httpOnly: true
    }
}));

always keep your application components up-to-date. Following are few tools that you can use to identify potential outdated components in your application.

And also keep your Node.js version and NPM up to date. Node.js version can be updated with “n” npm module.

sudo npm install n 
sudo n stable

You can update npm using npm (weired right?)

sudo npm install -g npm

Can we, effectively, build a program using this notion?

Meet your new friend:

 The Observable

Your friendly neighbor: RxJS

Reactive Extensions

Are a set of libraries for composing asynchronous and event-based programs using observable sequences and fluent query operators

Rx is everywhere

Java

JavaScript

.Net

Ruby

Swift

C++

Clojure

Scala

What is the most asynchronous?

User

More async data streams

  • XHR requests

  • WebSocket

  • Promises

  • Events

  • Dom Inputs

  • Web Worker

  • Web RTC

RxJS operators

RxJS operators

RxJS operators: map

resStream

RxJS operators: delay

resStream

RxJS operators: debounce

resStream

RxJS operators: merge

resStream

RxJS operators: combineLatest

resStream

RxJS operators: zip

resStream

Let's create a morse-code decoder!

Morse-code decoder

click...

click...

click.............

click...

Morse-code decoder

click...

click...

click.............

click...

Morse-code decoder

  • Dom events (keydown, keyup)

  • Start signal/End signal

  • Dot/Dash/Whitespace

  • Letter

  • Word/Sentence

Morse-code decoder

Morse-code decoder

  • "." = ~400ms

  • "_" = 3 * "."

  • between codes = "."

  • between letters = 3 * "." 

  • between words = 7 * "."

Morse-code decoder

const keyUps = Rx.Observable
                 .fromEvent(document, 'keyup');

const keyDowns = Rx.Observable
                   .fromEvent(document, 'keydown');

Morse-code decoder

const spaceKeyUps = keyUps.filter((data) => data.keyCode === 32);

const spaceKeyDowns = keyDowns.filter((data) => data.keyCode === 32);

Morse-code decoder

const signalStartsRaw = spaceKeyDowns.map(() => "start");
const signalEndsRaw = spaceKeyUps.map(() => "end");


const signalStartsEnds = Rx.Observable
                            .merge(signalStartsRaw, signalEndsRaw)
                            .distinctUntilChanged();

Morse-code decoder

const signalStarts = signalStartsEnds
                           .filter((ev) => ev === "start")
                           .timestamp();
const signalEnds = signalStartsEnds
                           .filter((ev) => ev === "end")
                           .timestamp();



const spanStream = signalStarts.flatMap((start) => {
    return signalEnds.map((end) => end.timestamp - start.timestamp)
                     .first();
});

Morse-code decoder

Morse-code decoder

const SPAN = 400;

const dotsStream = spanStream
                        .filter((v) => v <= SPAN)
                        .map(() => ".");

const lineStream = spanStream
                        .filter((v) => v > SPAN)
                        .map(() => "-");

Morse-code decoder

const dotsAndLines = Rx.Observable.merge(dotsStream, lineStream);

// [['.', '.', '-'], ['-', '.', '-'] ... ] 
const letterCodes = dotsAndLines.buffer(letterWhitespaces); 

 // ['A', 'B' ...]
const lettersStream = letterCodes
                        .map((codes) => morse.decode(codes.join("")));

Morse-code decoder

Morse-code decoder

What else?

Component bindings

Component bindings

click...

click...

click.............

click...

click

category.

comp.

bundle.

package.

Component bindings

Component bindings

Problems RxJS solves

  • Data Manipulation

  • Events handling

  • Callback Hell

  • Promise Hell

  • Comet/Async Manipulation

  • Race Conditions

  • Complex State

  • Tests 

RxJS problems

Complexity

Integration with imperative part of code

Documentation

Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences or Promises or array/iterable into one observable sequence.

Cascade Updating

Cascade Updating

Cascade Updating/Exceptions

RxJS with other frameworks

RxJS with Angular - rx.angular.js

angular.module('example', ['rx'])
  .controller('AppCtrl', function($scope, $http, rx) {

    function searchWikipedia (term) {
      return rx.Observable
        .fromPromise($http({}))
        .map(function(response){ return response.data[1]; });             
    }

    $scope.search = '';
    $scope.results = [];

    $scope.$createObservableFunction('click')
      .map(function () { return $scope.search; })
      .flatMapLatest(searchWikipedia)
      .subscribe(function(results) {
        $scope.results = results;
      });
  });

RxJS with Angular2 - inside

RxJS with React - rx-react

RxJS with others

Alternative?

Bacon

Kefir

Highland

I want more hardcore!

Javelin and ClosureScript

(defc test-results
  {:scores [74 51 97 88 89 91 72 77 69 72 45 63]
   :proctor "Mr. Smith"
   :subject "Organic Chemistry"
   :sequence "CHM2049"})

(defc= test-results-with-mean
  (let [scores (:scores test-results)
        mean   (/ (reduce + scores) (count scores))
        grade  (cond (<= 90 mean) :A
                     (<= 80 mean) :B
                     (<= 70 mean) :C
                     (<= 60 mean) :D
                     :else        :F)]
    (assoc test-results :mean mean :grade grade)))

Cycle.js

  • RxJS

  • Side effects/Logic

  • Composition

  • Streams, streams, streams, streams

Questions?

Made with Slides.com