> 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.
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.