Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How Not to Do Authentication in Node.js

How Not to Do Authentication in Node.js

This presentation gives examples of some bad practices when it comes to authentication in and for Node.js applications in general. Furthermore, the interactive code-along includes a way to demonstrate how to fix some of the problems herein contained using tools and methods which are not as complicated as people think.

Shreyansh Pandey

September 30, 2018
Tweet

More Decks by Shreyansh Pandey

Other Decks in Programming

Transcript

  1. MD5? So? “I-can-use-MD5-SHA-etc-because" starter pack: 1. It’s still a one-way

    hash function. 2. It’s secure. It can’t be broken. 3. I enforce strong password rules. 4. I like copying code.
  2. Few pointers: 1. It’s still a one-way hash function. Yes,

    it is. But that does not mean it is secure. 2. It’s secure. It can’t be broken. CVE-2012-3287 — md5crypt (b-crypt means blowfish-crypt).
  3. Do NOT read me: yes, you’re right. I created a

    new slide specifically for this xkcd. I enforce strong password rules.
  4. Buzzword — bcrypt/SHA-* • With SHA, it’s about implementation. FGPA

    or GPU. • SHA has a 32-bit design. • bcrypt is pretty decent at what it does: take more time.
  5. Viś-a-viś, the problem • Consider SHA-256. It’s implemented using a

    32-bit architecture. Very easy on GPU. • bcrypt, not so much. In-memory tables. Iterative recalculation. • GPU in the past, FPGA now. bcrypt can be exploited. • DDoS with bcrypt. Probable.
  6. Then what? • NIST SP-800-132. • Use PBKDF2 with high

    iteration count (Apple uses 10,000.)
  7. New kid on the block • Argon2* family of hashes.

    • One problem Hint: it’s overly complicated to implement.
  8. Barriers • Cryptography is hard. Without a mathematics degree, it’s

    Greek (literally). • There are some tutorials on how to do credential-storage but they aren’t real world examples. • PBKDF2: Microsoft, 1Password… (also Google?) • Don’t go about building your library*.
  9. The Solution •Try learning Cryptography. It’s not that hard. •Always

    be updated on the latest trends and 0- Day’s. •Don’t follow a tutorial (or a talk) blindly. •If you have some proprietary logic for credential storage, get it audited. •Always remember — in cryptography, you will most certainly need a tailor-made solution.
  10. That’s theory, what about tangible “stuff”? 1. Don’t overdo your

    length. It can lead to a DOS. (*) 2. Use an adaptive one-way function (PBKDF2, bcrypt, Argon2, etc.) with a cost-factor (“time delay”). 3. Use keyed functions like HMAC, etc. in conjunction with (2). 4. Never store plaintext passwords (evidently). 5. The key for HMAC should be treated like a private key; don’t store it in your database.
  11. Right… What about in JavaScript? • There are some really

    cool libraries which take out the guess-work: • libsodium (https://github.com/jedisct1/libsodium) • credential (https://github.com/ericelliott/credential) • node-argon2 (https://github.com/ranisalt/node-argon2)
  12. 1. Predictable tokens A. LCG, pseudo-random number generators, time-based function.

    B. Bruteforce. So, a large entropy? But that doesn’t mean it’s secure. Consider the substitution cipher. 2. Their value. They are as meaningful as passwords; then why are they not stored as such? 3. (Bonus: side-channel verifications. Security questions, rate limiting, audit records.)
  13. public function index() { $session = $this->request->session(); $login = $session->read('Administrators.id');

    if(!empty($login)) { return $this->redirect( ['controller' => ‘SecuredAdmin', 'action' => 'dashboard']); } $this->viewBuilder()->layout('admin_login'); $this->set(compact('user')); $this->set('_serialize', ['user']); } function login() { $username = $this->request->data['username']; $password = $this->request->data['password']; $admins = TableRegistry::get('Admins'); $row = $admins ->find() ->where(['username' => $username]) ->where(['password' => md5($password)]) ->first(); ...
  14. const payload = request.payload; if (payload.resettoken != '' && payload.resettoken

    != null) { User().filter({ resettoken: resettoken }).getJoin({ ‘reset’: true }).then(function(err, data) { if (err) { return { isSuccess: false } } return h.view('resetPassword', { resettoken: resettoken }); }) }
  15. app.handle('/', 'post', function(req, h) { User().filter({ resettoken: req.payload.resettoken }).getJoin({ 'reset':

    true }).then(function(err, data) { if (err) { return { isSuccess: false } } User.get(data.id).apply({ password: Helpers.calculatehash(password) }).then(function(err, data) { if (err) { return { isSucccess: false } } return { h.view('success-password-' + data.name); } }); }); });
  16. Sample Application The repository is divided into a couple of

    branches. 1. master — this contains a badly designed Node.js application. 2. better-storage — a better credential storage paradigm. 3. better-auth — a complete RESTful API with auth.