Another table, another database or another storage system in general.
If an attacker SQL injections your database don’t go spilling every hashed or unhashed password you’ve got.
I tend to store passwords in a separate keyvalue store from where my authentication identifier is (email, “username”).
If someone gets into my network they need to get into my servers with the email addresses and then get into a secondary system where the passwords are stored.
I like my password systems to be a k/v store because there is no need to “query” it. I usually store the password under a key that isnt the identifier. Instead using something like a database surrogate key.
Have a secondary system (microservice, private subnet) that simply returns a boolean representing if the provided non-email (key) and password (value) match.
Have that secondary system take the plain text password so it can do the hashing without letting the dependent service know what algorithm, salt or stretches you’re doing. This will also allow you to easily roll over to new hashing algorithms over time without affecting the service that is doing th authenticating.
Edit: I’m not trying to be a know it all or a crabby old tinfoil hat a-hole. But it’s passwords, man. When you leak them you ruin people’s days/year/life. Building that system above takes a middle of the road engineer a day or two. Put the effort in. Every password leak makes all of our jobs harder. It’s your companies responsibility to keep that safe. If you know that already, be the annoying guy that brings it up in every stand up. Make that debt known.
> don’t go spilling every hashed or unhashed password you’ve got.
Please don't store unhashed passwords. By now even PHP gives us the tools to do this right. You truly don't need secondary systems, or separate tables, or separate anything. Just hash the passwords.
I would argue that the benefit of putting all the hashes into a separate table is not really worth it. A separate service just to verify passwords sounds an awful lot like reinventing LDAP or AD/Kerberos with less features.
It should be good enough to simply encrypt the password hashes with an application-side key, a simple database dump won't leak passwords anymore.
If your passwords are properly hashed and stretched with appropriate and modern functions (SHA2/3 and Argon2) and encrypted-at-rest (Chacha20 or AES256) then you should be sufficiently equipped to secure your customers passwords against most attacks.
The point is not putting them in a system that can leak them right alongside the login identifier.
If someone wants to go exercise 9384828388 GPUs on your list. Fantastic, at least the other piece of their auth username/etc isn’t sitting right next to it.
Putting them in a separate system can be a simple REST server that sits in front of another database, LDAP or even something like Vault.
Don’t set them right alongside your ecommerce app you’ve got 30 juniors hacking on trying to get something out ASAP.
Why are your juniors hacking in production? They get a testing environment, if lucky they can play with staging but production should not be hacked upon.
I don't see the benefit of a seperate system still, if you really want to, LDAP already does all this. As does AD. Why reinvent the wheel and built a rest service for it?
I also don't know why it's harmful if a hash sits next to the username, if the database has been breached, password hashes are probably the least valuable information in such a leak. Mail addresses are more valuable.
They aren’t “hacking on production.” They’re occassionlly under pressure to release features. If they don’t get someone that catches a SQL injection or something else silly in a PR it’s nice to know we cant possibly bleed our password hashes.
Do you jam all your tables into one public/default schema? Or do you break them up by domain across schemas?
A very simple implementation of this in Postgres is an auth schema. Only one role can read from the table with the credentials, no app has access to that role. You write a function to verify a credential and use a security definer to give a role that can use that function access to read a single result.
In this case you’ve mitigated two “attacks”:
- a sql injection selecting all from users
- some random engineer taking a schema dump for whatever purpose, accidentally grabbing auth details and then having that get leaked somehow down the line.
I literally takes a few lines of SQL to set that up. It’s not an extra server to manage. Just a different schema with higher security constraints.
From your app with the credentials to call that compare function instead of doing a SELECT FROM you can call that function with the credentials and just accept Postgres telling you yes or no the credentials matched.
PG has a suite of hashing functions. Take your pick. That function can do your salting or you could add it as a parameter to the call.
SQL Injection is to my knowledge not a problem if you use literally any query building method but string concatenation.
Any sane SQL driver offers queries with parameters and at work and in private I enforce the usage of parameters in queries entirely. If I find myself needing string concat anyway then I will do it very carefully and isolated from user input.
That mitigates SQL injection.
I also see no benefit in breaking it up into multiple schemas or pushing authentication entirely into SQL. At best that means you complicated the database definition unnecessarily and secondly it means your database gets raw password strings which is contra to what I learned about password security, namely that the raw password does not touch the DB or the DB driver or any DB related code. Ever.
I also don't see how engineers dumping the DB and then accidentally leaking it is a tangible risk. I'd put that on the bottom of my list of things I have to worry about when I verify passwords.
Not everyone works with PG either or even MySQL or there is no possibility of setting up functions or triggers in the database.
>If they don’t get someone that catches a SQL injection or something else silly in a PR it’s nice to know we cant possibly bleed our password hashes.
Which is totally irrelevant if you properly secure the application, do proper code reviews and don't put juniors under pressure to push out code at all costs.
Pushing crappy code because deadline is asking for it. End of story.
The fantasy world you work in must be great. I’m jealous.
I don’t know how many times I’ve seen someone jam a gigantic pile of string into a query builders select method or done some whack string interpolation trying to make a clever search form instead of using a tool like ransack or the equivalent.
Like I said, that was a for instance using Postgres, you can do it plenty of ways. Hashi Vault has been an awesome tool for this recently.
Getting the password near your database isn’t the problem. It’s persisting it unhashed that is. I’d conjectire a fair amount of sites terminate SSL at the edge of their network (nginx, ALB) and they have a password floating through the stack in plain text until it hits a compare method. What does it matter if that method is in your app code versus pg or vault or any tool you’d use for credential storage? Are you sure your app is scrubbing all the json keys or http field names that could contain a password to make sure it’s not going into your rails/node/whatever logs or into your nginx access logs?
Having multiple schemas isn’t complication, it’s organization.
As far as people doing dumb shit with files they’ve dumped or downloaded. It happens.
When you’re designing a system for storing credentials you don’t know who will work on this system or what practices will be in place around code quality, reviews and deadlines when you’re gone. You can’t literally watch every PR or guarantee that ever code review has someone as smart as Zaarn presiding over it.
You should be designing something people can’t shoot themselves in the foot with.
An example of a totally reasonable mistake to make i saw recently in a PR in a client I work with.
They had designed a pretty solid API using the JSONAPI spec. JSONAPI if you aren’t familiar allows an API client to request what fields they need from the endpoint instead of just getting all the fields the API returns. This is nice for mobile clients.
They had the forethought to exclude allowing people to ask for “password” through the /users API. Nice. Nailed it.
A year passes.
They add a feature for changing your password that required you to confirm by email that’s the password change was you and then it swapped the password from the “new_password” column to the “password” column. While this PR was in staging you could do /users?include=new_password and it’d return their new password (hashed, but still).
Password resetting was a feature they were adding to their registration and auth code.
The people that worked on that front end didn’t necessarily work on the user API and weren’t familiar with how it worked. But by adding this flow and this field the created a vulnerability in a tangentially related system.
Now I’m sure you could crap on JSONAPI but it serves a purpose. If they hadn’t been using that someone just as lazily could have designed the regular JSON endpoint to return all fields but password and they’d have ended up in the same boat.
The takeaway from that (which has been known for a decade at least) is that your passwords need to be hashed and salted, and you need to use a hashing algorithm designed for security (meaning it is slow). Not that you should complicate your system architecture and create more potential points of attack.
Absolutely, Im not stating otherwise. Im saying you shouldnt have your authentication inside your business logic. It should be a separate service. Limited access, and transparent to the application that is calling on it.
If you want that to be a REST service, great.
If you want it to be another table/schema/database/server, great.
If you want it to be something else, great.
Just stop putting it right inside the application that is taking user input.
Don't roll your own hashing, use something solid, like Argon2, salt your stuff. Store your salt someplace secure, not in your frigging rails config.yml or inside env on your webservers or anything else that is publicly addressable.
> Im saying you shouldnt have your authentication inside your business logic. It should be a separate service.
And I am saying this is a wrong, counterproductive idea. It complicates your authetication for no substantial gain, and will only result in additional vulnerabilities.
> Store your salt someplace secure, not in your frigging rails config.yml or inside env on your webservers or anything else that is publicly addressable.
So you don't even understand how a salt works. Why should we take security advice from you again?
Using something like Hashi Vault, Gluu, or Shiro does not overly complicate things. They’re stable, trusted solutions.
It can also greatly simplify things. In the common scenario of having a web app for customers and a web app for admins, instead of them each having their own authentication baked in, you can choose an open source solution and deploy it twice, once for each service.
I know how salt works. Don't belittle me.
If someone gets into your web servers they’ve pretty much got carte blanche at your database the web server has access to. They also now have your code, which in a lot of cases is probably plainly readable. So they can see your salt, see how you salt and see the hashing algorithm you've chosen.
I’m saying don’t store the salt in your apps config right along with the credentials to access all of your login identifiers and hashed passwords.
You gave them a piece of the puzzle. If someone wants to grind through and crack all the salted/hashed passwords, I'd prefer them not to have the salt to help in that endeavor.
He's talking about how each password should use a separate salt. This is normally stored next to the password hash. Many hashing algo implementations will even do this for you.
All security decisions boil down to your threat model, doesn't it? At some point there are decreasing marginal returns with increasing security. What threat models are you satisfying with the strategies your proposing? Do you think these apply to everyone? Or even everyone who stores hashed passwords? I personally don't think so, but I'm interested in hearing about things I haven't come across yet.
A day of engineering effort to make exposing passwords much more difficult. Yes. If you’re a company that stores someone’s password. You know they probably use it elsewhere so it’s your responsibility to help keep it safe.
Sorry, but this is convoluted nonsense that can only achieve one thing: make yourself more vulnerable.
You want your security system to be as simple as possible, and to involve as little custom code as possible. Because you can and will fuck it up if you try to be clever.
Hash and salt your passwords using a library designed exactly for that purpose (which means it will use a slow hash). That's it, end of story.
Rule n1: don't roll your own security.
Rule n2: goto 1
You are overcomplicating your authentication system by oversimplifying security problems and the result is that you have solved nothing.
Security always seems very easy to solve and usually non-security engineers tends towards solutions like yours that doesn't provide extra security, they just add a few extra steps for a hacker to obtain you database and as a result you need to maintain extra databases, there are more error points... Do you remember that thing about "each extra system exponentiates complexity"?
go a step further and you will get into key management devices like the HSMs and/or the Amazon KMS. KMS cost almost next to nothing and it is pretty neat since its a web service, especially coming from the world of $40k+ Thales/Safenet HSM devices which are a pain to deal with (backups, rehash, redundancy).
It was to meet the PCI-DSS Level-1 security standards for banking compliance. We'd store encrypted cards in one place and store the master keys in the AWS KSM to later decrypt it. But to retrieve the master keys, it goes through another layer of encryption.
Another table, another database or another storage system in general.
If an attacker SQL injections your database don’t go spilling every hashed or unhashed password you’ve got.
I tend to store passwords in a separate keyvalue store from where my authentication identifier is (email, “username”).
If someone gets into my network they need to get into my servers with the email addresses and then get into a secondary system where the passwords are stored.
I like my password systems to be a k/v store because there is no need to “query” it. I usually store the password under a key that isnt the identifier. Instead using something like a database surrogate key.
Have a secondary system (microservice, private subnet) that simply returns a boolean representing if the provided non-email (key) and password (value) match.
Have that secondary system take the plain text password so it can do the hashing without letting the dependent service know what algorithm, salt or stretches you’re doing. This will also allow you to easily roll over to new hashing algorithms over time without affecting the service that is doing th authenticating.
Edit: I’m not trying to be a know it all or a crabby old tinfoil hat a-hole. But it’s passwords, man. When you leak them you ruin people’s days/year/life. Building that system above takes a middle of the road engineer a day or two. Put the effort in. Every password leak makes all of our jobs harder. It’s your companies responsibility to keep that safe. If you know that already, be the annoying guy that brings it up in every stand up. Make that debt known.