How do you mitigate credential stuffing attacks? Throttling per account? Globally? By source IP?
A user leaves their company. How do you automatically deactivate their account?
You're using password hashes. Cool. A secure password hash function, right? Scrypt, argon2, maybe bcrypt? (MD5, SHA1, and SHA2 are not password hashes.)
You're comparing them in constant time, right? Are you sure? The compiler hasn't "optimized" you into problems here?
You're working with enterprise websites. You obviously support 2FA; and you're probably going to want to support SSO with SAML2, and definitely integrate with Active Directory over LDAP.
And your password reset flow- just send an email with a magic link? do you generate the magic link's token with a CSPRNG? Is it long enough? When does it expire? And admins probably have to be able to reset anybody's password, but with strong auditing and accountability features...
You store user info in the session information- basically caching it out of the database? How do you invalidate sessions after a user changes their password or has their account disabled?
Can somebody put Unicode in their password?
You're writing a POST handler. You protect against CSRF, right? (yes, tricking somebody into logging in as another user can be very useful!)
Doesn't seem so simple to me...
Good TLS and salt + strong hash solve the simple cases of DB theft and network MITM... But that's the bare minimum amount of protection.
As it does nothing for tokens being stolen, sessions, password reset hacks and emails being compromised.
It does nothing for stopping users trying to brute force. Password strength, loads of things mentioned.
I think the point is not really needing to know what good libraries mitigate against, but that you probably (as an individual) don't know all the ways that things can be compromised, and can't easily remember to implement them all, so you want a system that has been created, tested and improved by many security conscious people and out in the wild.
You probably wouldn't build your own lock for your front door either. The risks simply outweigh the benefits.
> Good TLS and salt + strong hash solve the simple cases of DB theft and network MITM...
It doesn't even solve this that. If you're explicitly salting your password hashes and not using a library, you're probably doing something very wrong. Unless you're hashing your passwords with an expensive key stretching function such as bcrypt or scrypt (or even better, a CPU hard hash like argon2) anyone who get their hands on your database can very cheaply bruteforce about 90% of your passwords (all but the most expensive ones).
A salt will only protect you from rainbow table attacks, and while that's useful, since computing SHA-2 on a GPU is so mind-bogglingly fast today, it just doesn't mean much anymore.
More eyes on security, can also mean a short time of eyes by a single person on a single problem - missing the bigger picture. Adding to the problem, security researchers are very few and very busy (I've hired). They don't have time to look at open source code, unless there's a possible reward (that's big enough).
Luckily software is cheaper to implement strongly, but still email + password is usually selected as the method in spite of being the easiest thing to crack (as far as getting into individual accounts is concerned).
We could get users to all use ubikeys or more complex forms of multiple factor auth.
We could make it so that if you compromise an email account, you can't just reset all the external account passwords using it.
Plenty of sites allow insecure passwords that are in rainbow tables and cannot defend against targeted attacks (unless a user actually selects a strong password themselves).
Also just as houses often have big windows that can be smashed, perfect crypto / secure auth practices fail to work with the presence of an exploit somewhere else in the stack are frequent sources of compromise too.
It's not a perfect analogy of course, but actually I don't think locks / houses is a bad one. Users don't value security enough to want the most secure locks on their accounts and frequently get annoyed if asked to use 2-factor Auth, multiple passwords for single sites etc.
Though it doesn't hurt to do anyway :)
It is important when comparing (some forms of) auth tokens, such as csrf tokens.
[1] http://www.darkreading.com/vulnerabilities---threats/php-has...
Reliable implementations for hash functions are available for literally any language that is used in production. They take care of all the problematic parts. (In fact nobody should write this by himself or herself)
I'm sure as a custom implementer it's still possible to accidentally sneak in something time-dependent (still difficult to exploit, with smart rate limiting in place, I would say forget it) or a null/whitespace bug (easy to exploit but might require custom tooling). But that's just a matter of testing and having a 2nd pair of eyes look over it.
How do you know an off-the-shelve solution doesn't have the same problems or different ones? With a limited time/money budget auditing someone else's code is far more effort than doing the same thing with the own code.
- limiting login attempts with a die off time between tries
- blocking automated brute force login attacks
- verifying accounts by email on sign up / invitation
- verifying username and password changes by email
- a friendly forgotten password workflow
- supporting even one basic 2FA factor (especially important on enterprise)
- logging user activity
- permission management
You could easily write all those things yourself. None of them are very complicated. But if you get any of them wrong it's very, very bad. Why wouldn't you use something that thousands of people have tested already?
> Why wouldn't you use something that thousands of people have tested already?
I think some of us find the most portable thing between projects -- across languages and platforms -- are outlines of what some part of an application has to be able to do and an ability to implement it from scratch.
There's certainly always some level at which off-the-shelf is a more productive and reliable choice. A DIY approach tends to yield more dividends to the degree that constraints or requirements in play would compel you to work against the grain of off-the-shelf stuff (thereby having to deal in the details anyway, not only for the problem, but for how the frameworks does things), and fewer dividends (or outright liabilities) to the extent that off-the-shelf will more than cover you.
Additionally, it's good to remember that something like Bob Martin's observation can apply: "framework authors are out to screw you -- that's not quite true, they're not exactly out to screw you, but they will screw you because the framework authors have their interests in mind, not yours." The language is a little bit over the top but it's true that dependencies are not solid foundation points, they're viscous potential points of future instability because they're projects driven by incentives that are not always aligned with yours. And the closer they are to the center of your app the more that might matter at some point. What happens with authentication if/when you decide to migrate frameworks? Or if your framework-supported application is only one of several (not all based on the same stack!) providing service in front of a common datastore?
It's worth reckoning with the overhead and hazards of DIY for sure, it's just also worth reckoning with the sometimes more subtle costs of framework buy-in.
I'll also add that while there are times I'm an enthusiastic supporter of framework use, I can't imagine being anything other than skeptical about outsourcing authentication to a third party service. Maybe there's a circumstance in which I'd find that would make more sense someday, but placing such a core thing in third-party hands gives me the shivers.
Just to clarify, I said framework not service. I'm advocating in favor of using things like Passport.js that you run entirely yourself. I'm not advocating for auth services like Auth0 or Firebase Auth (which have there place, but require a lot more consideration beforehand).
As you mention handling multiple Oauth providers and stuff that is usually not worth the effort without a library, but anyway. Just looking out for people like the younger me (who would probably have tried to roll out things they shouldn't have, naively).
Auth is ridiculously complex, is the source of MANY critical security defects, has plenty of frameworks which solve significant parts of it, and always seems easy until someone explains to you what all you didn’t know you needed.
Components:
- login page
- two factor state
- password reset form
- username lookup form
- logic for 401 status
- logic for 403 status
- throttling, is ban, user lockouts, and/or captchas for the forms you don’t want attacked at scale
- if you are storing credentials on your server (like password hashes), you better know what the options are for hashes, what a salt is, have investigated bcrypt and scrypt, know what happened when any of the thousands of large websites had their user databases leaked
- maybe support warning users that their credentials in your DB matches a record in HaveIBeenPwned and lock down their account until they password reset using their email address
- have a plan for when the hash you chose is deprecated in a few years while you aren’t paying attention and need to spent days re-coding to support new hash and legacy hash users, auto-migrating the legacies on next login
- want to support multiple 3rd party IDPs as well as local password auth? That starts to explode complexity and the number of forms.
- want to support SSO for enterprise? Use a framework that already supports SAML integration
I think a lot of professionals (not just programmers) get stuck in phase 2. This is where, in our field, most of the code comes from: capable, smart people writing new code furiously, and with the not incosequential fact that it provably works. Sure, it has bugs, but you can get to those eventually. It also happens to be the most "defensible" phase, where you get to speak down to others - you have concrete experience, real knowledge, so you are justified. I would guess this is also the bulk of working programmers.
So, the OP's solution could be coming from a phase 1 or phase 3 place. You are certainly coming from phase 2. I would encourage you to stretch a bit and consider when the OP's simple solution makes sense, and when it doesn't. I would hope you would do this in an interview as well, because you'll find phase 3 people saying all kinds of whacky things, which might not make sense to you unless you ask.
(A careful phase 3 will make sure to preface his simple solution with his limits, and to not do so is a little bit sloppy, but personally I don't think that's a deal-breaker.)
I can't think of a situation where OP's solution makes sense. Except for the obvious cases (app will never be in production, will never have real users or contain valuable data, etc)
In any event, I also believe there is a generally better approach to auth that avoids a lot of the complexity of traditional approaches. It's speculative, but see my other comment in this thread: https://news.ycombinator.com/item?id=22157951