Tipy pro zabezpečení Express.js

TL;DR

Tento text je součástí mé nové knihy Pro Express.js:Master Express.js — The Node.js Framework For Your Web Development [Apress, 2014]. Bezpečnost je důležitá, proto jsem se rozhodl tuto kapitolu zveřejnit na svém blogu. Kniha bude vydána velmi brzy.

Sada tipů v této kapitole se zabývá bezpečností v aplikacích Express.js. Bezpečnost je často opomíjeným tématem, které se odkládá na poslední chvíli před vydáním. Je zřejmé, že tento přístup, který považuje zabezpečení za dodatečný nápad, je náchylný k ponechání děr pro útočníky. Lepší přístup je zvážit a implementovat bezpečnostní záležitosti od základů.

JavaScript prohlížeče si vysloužil špatnou pověst kvůli chybám zabezpečení, takže musíme naše aplikace Node.js udržovat co nejbezpečnější! Pomocí jednoduchých úprav a middlewaru, které jsou popsány v této kapitole, můžete bez námahy vyřešit některé základní bezpečnostní problémy.

Tato kapitola pokrývá následující témata:

  • Padělání požadavků mezi stránkami (CSRF)
  • Zpracovat oprávnění
  • Záhlaví zabezpečení HTTP
  • Ověření vstupu

Padělání požadavků napříč weby

CSRF a csurf middleware byly stručně popsány v kapitole 4 Pro Express.js. Definici a vysvětlení CSRF naleznete v této kapitole.

csurf middleware dělá většinu práce při porovnávání příchozích hodnot z požadavků. Stále však potřebujeme vystavit hodnoty v odpovědích a předat je zpět serveru v šablonách (nebo JavaScript XHR). Nejprve nainstalujeme csurf modul jako každá jiná závislost s:

$ npm install [email protected]

Poté použijeme csurf pomocí app.use(), jak je popsáno v kapitole 4:

app.use(csrf());

csrf musí předcházet cookie-parser a express-session protože to závisí na tomto middlewaru (tj. nainstalovat, importovat a aplikovat potřebné moduly).

Jedním ze způsobů, jak implementovat ověření, je použít vlastní middleware k předání tokenu CSRF všem šablonám pomocí response.local . Tento vlastní middleware musí předcházet trasám (jako je tomu u většiny příkazů middlewaru):

app.use(function (request, response, next) {
  response.locals.csrftoken = request.csrfToken();
  next();
});

Jinými slovy, ručně usnadňujeme přítomnost tokenu v těle (v tomto příkladu), dotazu nebo záhlaví. (V závislosti na vašich preferencích nebo smlouvě mezi klientem můžete použít dotaz nebo záhlaví.)
Pro vykreslení hodnoty v šabloně jako skryté hodnoty formuláře můžeme použít

input(type="hidden", name="_csrf", value="#{csrftoken}")

Toto skryté vstupní pole přidá hodnotu tokenu k odeslaným datům formuláře, což usnadní odeslání tokenu CSRF na /login trasa spolu s dalšími poli, jako je e-mail a heslo.

Zde je úplný obsah jazyka Jade v souboru ch15/index.jade:

doctype html
html
  head
    title= title
    link(rel='stylesheet', href='https://m03s6dh33i0jtc3uzfml36au-wpengine.netdna-ssl.com/css/style.css')
  body
    if errors
      each error in errors
        p.error= error.msg
    form(method="post", action="/login")
      input(type="hidden", name="_csrf", value="#{csrftoken}")
      input(type="text", name="email", placeholder="[email protected]")
      input(type="password", name="password", placeholder="Password")
      button(type="submit") Login
    p
      include lorem-ipsum

Chcete-li vidět ukázku CSRF v ch15/app.js, spusťte server jako obvykle s $ node app . Poté přejděte na domovskou stránku na adrese http://localhost:3000. Token byste měli vidět ve skrytém poli formuláře, jak je znázorněno na obrázku 15-1. Mějte na paměti, že hodnota vašeho tokenu se bude lišit, ale jeho formát bude stejný.

Obrázek 15-1. CSRF token z csurf modul vložený do formuláře pro pozdější odeslání na /login trasa

Za každý požadavek na domovskou stránku (/) nebo obnovení stránky získáte nový token. Pokud však token rozšíříte, abyste simulovali útok (můžete to provést přímo v nástrojích pro vývojáře Chrome), zobrazí se tato chyba:

[Sidenote]

Čtení blogových příspěvků je dobré, ale sledování videokurzů je ještě lepší, protože jsou poutavější.

Mnoho vývojářů si stěžovalo, že na Node je nedostatek dostupného kvalitního videomateriálu. Sledování videí na YouTube je rušivé a platit 500 $ za videokurz Node je šílené!

Jděte se podívat na Node University, která má na Node ZDARMA videokurzy:node.university.

[Konec vedlejší poznámky]

403 Error: invalid csrf token
  at verifytoken...  

Zpracovat oprávnění

Je zřejmé, že je obvykle špatný nápad spouštět webové služby jako root. Vývojáři operací mohou využít authbind Ubuntu k navázání na privilegované porty (např. 80 pro HTTP a 443 pro HTTPS), aniž by udělili root přístup.

Případně je možné zrušit oprávnění po navázání na port. Myšlenka je taková, že předáme hodnoty GID (ID skupiny) a UID (ID uživatele) do aplikace Node.js a použijeme analyzované hodnoty k nastavení identity skupiny a identity uživatele procesu. Toto nebude fungovat ve Windows, takže možná budete chtít použít if/else a process.platform nebo NODE_ENV aby byl váš kód multiplatformní. Zde je příklad zrušení oprávnění nastavením GID a UID s vlastnostmi z process.env.GID a process.evn.UID environmentální vars:

// ... Importing modules
var app = express();
// ... Configurations, middleware and routes 
http.createServer(app).listen(app.get('port'), function(){
    console.log("Express server listening on port "
    + app.get('port'));
    process.setgid(parseInt(process.env.GID, 10));
    process.setuid(parseInt(process.env.UID, 10));
});

Záhlaví zabezpečení HTTP

Middleware Express.js s názvem helmet (https://www.npmjs.org/package/helmet; GitHub:https://github.com/helmjs/helmet) je kolekce middlewaru souvisejícího se zabezpečením, který poskytuje většinu bezpečnostních hlaviček popsaných v článku Recx „ Sedm hlaviček HTTP webového serveru, které zdarma zlepšují zabezpečení webových aplikací.“

V době psaní tohoto článku helmet je ve verzi 0.4.1 a obsahuje následující middleware:

  • crossdomain :Obsluhuje /crossdomain.xml zabránit Flash v načítání určitého nežádoucího obsahu (viz www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html)
  • csp :Přidá zásady zabezpečení obsahu, které umožňují načtení obsahu na seznam povolených
    (viz content-security-policy.com a www.html5rocks.com/en/tutorials/security/content-security-policy)
  • hidePoweredBy :Odstraní X-Powered-By, aby se zabránilo odhalení, že používáte Node.js a Express.js
  • hsts :Přidává HTTP Strict Transport Security, aby se zabránilo prohlížení vašich webových stránek přes HTTP (místo HTTPS)
  • ienoopen :Nastaví hlavičku X-Download-Options pro Internet Explorer 8+, aby se zabránilo načítání nedůvěryhodného HTML v prohlížečích IE (viz blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part- v-comprehensive-protection.aspx)
  • nocache :Záhlaví Cache-Control a Pragma pro zastavení ukládání do mezipaměti (užitečné k odstranění starých chyb z prohlížečů uživatelů)
  • nosniff :Nastaví správnou hlavičku X-Content-Type-Options ke zmírnění čichání typu MIME (viz msdn.microsoft.com/en-us/library/gg622941%28v=vs.85%29.aspx)
  • xframe :Nastaví záhlaví X-Frame-Options na DENY, aby se zabránilo umístění vašeho zdroje
    do rámce pro útoky clickjacking (viz en.wikipedia.org/wiki/Clickjacking)
  • xssFilter :Nastaví hlavičku X-XSS-Protection pro IE8+ a Chrome na ochranu před útoky XSS (viz blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter- with-the-x-xss-protection-http-header.aspx)

Chcete-li nainstalovat helmet , jednoduše spusťte:

$ npm install [email protected]

Importujte modul jako vždy:

var helmet = require('helmet');

Poté použijte middleware před cestami. Výchozí použití je následující (ch15/app.js):

app.use(helmet());

Obrázek 15–2 ukazuje, jak helmet v0.4.1 HTTP odpověď bude vypadat při použití s ​​výchozími možnostmi:

Obrázek 15–2. helmet v0.4.1 HTTP odpověď při použití s ​​výchozími možnostmi

Ověření vstupu

Express.js neprovádí žádnou sanitaci nebo ověřování vstupu uživatele/klienta, když jako vstupní data používáte analyzátor těla nebo dotaz. A jak všichni víme, nikdy bychom neměli věřit vstupu. Do vašeho systému lze vložit škodlivý kód (injekce XSS nebo SQL). Například kód JavaScript prohlížeče, který považujete za neškodný řetězec, se může změnit v útok, když tento řetězec vytisknete na svou stránku (zejména v případě, že váš šablonový modul automaticky neunikne speciálním znakům!).

První linií obrany je ruční kontrola dat pomocí regulárních výrazů na trasách, které přijímají externí data. Další „obranu“ lze přidat do objektově-relační mapovací vrstvy, jako je Mongoose Schema (viz kapitola 22 Pro Experss.js).

Pamatujte, že ověření front-endu/prohlížeče se provádí pouze pro účely použitelnosti (tj. je uživatelsky přívětivější) – váš web před ničím nechrání.

Například v ch15/app.js můžeme implementovat ověření, které používá vzor RegExp v poli e-mailu, příkazy if-else a metodu test() k připojení chybové zprávy k poli chyb takto:

app.post('/login-custom', function(request, response){
  var errors = [];
  var emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

  if (!request.body.password) errors.push({msg: 'Password is required'});
  if (!request.body.email || !emailRegExp.test(request.body.email) ) errors.push({msg: 'A valid
email is required'});
  if (errors)
    response.render('index', {errors: errors});
  else
    response.render('login', {email: request.email});
});

Jak přidáte další trasy a vstupní pole k ověření, skončíte s více vzory RegExp a příkazy if/else. I když to bude fungovat lépe než bez ověření, doporučujeme napsat vlastní modul nebo použít express-validator .

Chcete-li nainstalovat express-validator v2.4.0, spustit:

$ npm install [email protected]

Importujte express-validator v ch15/app.js:

var validator = require('express-validator');

Poté použijte express-validator po body-parser :

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(validator());

Nyní se v rámci obslužných rutin požadavků dostáváme k přístupu request.assert a
request.validationErrors() :

app.post('/login', function(request, response){
  request.assert('password', 'Password is required').notEmpty();
  request.assert('email', 'A valid email is required').notEmpty().isEmail();
  var errors = request.validationErrors();
  if (errors)
    response.render('index', {errors: errors});
  else
    response.render('login', {email: request.email});
});

index.jade soubor jednoduše vypíše chyby z pole, pokud nějaké jsou:

if errors
  each error in errors
    p.error= error.msg

A login.jade šablona vytiskne e-mail. Tato šablona se vykreslí pouze v případě, že ověření proběhlo úspěšně.

 p= email

Pro demonstraci přejděte na domovskou stránku a zkuste zadat nějaká data. Pokud se vyskytnou chyby, zobrazí se vám domovská stránka s chybami, jak je znázorněno na obrázku 15–3. Dvojitá zpráva „Je vyžadován platný e-mail“ pochází ze skutečnosti, že pro pole e-mailu máme dvě tvrzení (notEmpty a isEmail) a obě selžou, když je pole e-mailu prázdné.

Obrázek 15–3. Chybové zprávy z používání express-validator k uplatnění hodnot formuláře

Shrnutí

Bezpečnost je prvořadá, ale často opomíjená. To platí zejména v raných fázích vývoje. Typický myšlenkový proces vypadá takto:zaměřme se na poskytování více funkcí a o zabezpečení se postaráme později, až se chystáme vydat. Toto rozhodnutí je obvykle dobře zamýšleno, ale jen málokdy dopadne podle plánu. V důsledku toho trpí bezpečnost systémů.

S middlewarovými knihovnami, jako je csurf , helmet a express-validator , můžeme získat dobré množství základního zabezpečení, aniž bychom museli přidávat příliš mnoho vývojových cyklů.

V další kapitole přeřadíme rychlost a probereme některé přístupy k používání Express.js s knihovnou Socket.IO pro reaktivní (tj. aktualizované v reálném čase) zobrazení…

Pokud se vám tento příspěvek líbil, možná budete chtít prozkoumat další úryvky z Pro Express.js:Master Express.js – Node.js Framework for Your Web Development jako například:

  • LoopBack 101:Express.js na steroidech
  • Sails.js 101
  • Secret Express.js Settings

Samotná kniha bude velmi, velmi, velmi brzy odeslána k tisku.