Hacker News new | ask | show | jobs
by mantrax3 4462 days ago
Many things said in that video are kinda off.

He mixes password hashes and MAC (message authentication codes), saying we should HMAC our password hashes. There's little to gain from that:

- Password hashes exist to hide passwords. The attack is to discover the original password by brute force, dictionary, rainbow tables etc. The protection against this is irreducibly slow hash algorithms and unique public salt per hash.

- MAC exists to authenticate message origin. Unlike passwords, the message is often public. The attack is therefore not to discover the message, but to manipulate the message, yet make it appear from the same origin as the original message. The protection against this is the HMAC algorithm with a secret key that only the original message author has.

HMAC-ing your password hash won't stop, or even significantly slow down any of the attacks performed against password hashes, so it feels cargo cultish to claim it's just magically more secure by using it. It's even more WTF-y to claim a password hash library should offer HMAC, or it's insecure.

Once a system is compromised, and the attacker doesn't care to recover the original password, it's far more trivial to just replace the hash or just directly read the data protected by the login directly in the DB, rather than play out a MAC attack on a pass hash. It makes no sense.

HTML escaping. You either escape for HTML or you don't. There's no sane way to escape some of it. His idea that we should be able to escape some tags, but skip (or "strip") others is a huge vector of attack as HTML is notoriously filled with edge cases that your selective escaping/stripping engine won't have. It's not trivial to parse what's a tag and what isn't a tag to a browser, trust me. It only looks trivial if you're ignorant.

If you want to allow some tags, and disable others (say, allow bold/italic on forums, but nothing else) you should go through a strict DSL allowing only those types of formatting, which are then converted to HTML. Just like HackerNews does it - I type star-word-star and I get "word" in italic. That's a DSL. That DSL could be made to look like HTML, but it won't be HTML. It's fully parsed and rebuilt from the DSL syntax tree. So it can be made safe. Selective HTML escaping without those crucial steps is almost impossible to make safe by comparison.

His rant about "there are too many templating engines - we should stop" - yeah, good luck saying we should stop and this working out.

Likewise about databases. "Let's just stop". B.S. The thing he's missing is that different DB engines have different strengths and weaknesses. That's one major reason there are so many, and why many get used in the same project. PgSQL isn't SQLite isn't MongoDB isn't Redis.

By enforcing one type of database for multiple separate project components, you don't improve security, you just cripple architecture and performance.

2 comments

I'm pretty sure the intention behind something like this:

    (def a-password (-> pass
                        (hmac server-stored-key)
                        (scrypt-kdf work-factor))
is to prevent all information necessary to calculate the stored hash being on the same system where the hash is stored. By HMAC'ing and not storing that key in the database (but on the app server), you now make the attacker pull off two attacks.

You could replace HMAC with encrypt with AES (or scrypt for that matter) to get the same practical effect.

Edit: clarification

I think this is actually works. Might want a configurable work factor for the kdf.

    (ns passwords
      (:require [pandect.core :refer [sha256-hmac]]
                [crypto.password.scrypt :as scrypt]
                [environ.core :refer [env]])) 

    (def SECRET-KEY (if-let [key (env :secret-key)]
                       key
                       (throw "Set your SECRET_KEY!!!")))

    (defn encrypt-password 
      [pass] 
      (-> pass (sha256-hmac SECRET-KEY) scrypt/encrypt))
Edit: update crypto-password code to actually call encrypt.
The problem of lookup table attacks is solved by the public salt I already mentioned. HMAC protects against attacks like length extension (google it), which aren't applicable for discovering a password from a hash.

Encryption also does nothing here, because it's reversible.

Your encryption key is somewhere on your server, so whatever you encrypted (either the plain text password, or the hash) can be decrypted once, and then if it's a hash you just attack the bare hash as usual (brute force, dictionary etc.).

Instead of encryption you can just store part of the salt outside the DB, but with a good hash this is usually not needed and just pointless obfuscation.

Consider this scenario (which is extremely common):

Public access to a SQL database. Input isn't properly sanitized while building some complicated query. $badnick executes a SQL injection. Luckily, the database user from the public site only has SELECT privileges (yay SOA or something). This is an extremely common enterprise situation.

The attacker can dump data (like password hashes), but in an HMAC'd password setup, cannot actually gain access to modify or login as a user via brute-forcing. They cannot pivot the user account to access to another system. The attacker must find a different hole to gain access to the app/login server.

Well first of all your "common scenario" terrifies me, and betrays an app developed by incompetent devs who wouldn't even know what a "hash" is, because in any sane situation:

1) The SQL database is not public (any more than it's common to have public anonymous FTP to your server's app deployment). It listens on a specific interface, specific port, from specific IPs with a specific user and password (which is not "root"/"").

2) Input isn't properly sanitized? Hello? If that's "common" then fix that first. It's not 1998 anymore, no one has an excuse for that level of ignorance. First of all "sanitize" is an incorrect term for what needs to be done. You either escape an SQL literal, or you pass it as a parameter to a prepared statement. You do this to all data, all DB layers provide escaping, and it's absolutely trivial to do. And for prepared statements, there is no way to do it wrong, because it's not you who does it. Data and query in prepared statements are separated at the protocol itself and injection is impossible, you can't possibly mess it up. All of this is "using databases 101". Heck, your app will break even with innocent data containing, say, a quote, if you fail those.

3) I mentioned twice already, there's public salt for hashing the password, and there's irreducibly slow hash to be used to protect against a weak password (like Blowfish). This is what breaks brute-force attacks, not HMAC.

4) Let's assume your exact scenario is indeed terrifyingly common. Still, HMAC does nothing at all here, that keeping part of the salt off-the-DB won't do. HMAC doesn't mean "when you have a key outside the DB". You don't need HMAC to keep part of the salt outside the DB. Just... keep part of the salt outside the DB, without HMAC.

But if you feel the need to obfuscate your code like this, it means you're working in hell and some people don't deserve their jobs, and better fix that, before you start thinking about compensating for it at the wrong place of the app.

I should clarify that when I say "public access to", I mean specifically that a public person can access an application talking to the SQL database.

I.e. User <-> App <-> Database

Input is simply not properly sanitized in the real world because mistakes happen. This isn't by design. This is a source of remote execution. The remote execution only yields read access since the app only needs read access (and thus the fewest privileges possible are read access). The attacker can read password hashes, but cannot update them.

As I mentioned before, HMAC is just an example of keeping all information necessary to login to the site out of the stored hash. Any non-constant value per password will do (feel free to maintain a lookup table of unique lists to active passwords in your app if you feel you must, I'll be HMAC'ing the user's input password against a constant secret key before sending to KDF). The idea is that an attacker who can read your database (but not the application code) cannot gain access as a user without first also compromising the application code.

Once again there's no way to make a mistake with a prepared statement. The only way to do it is using poor practices like using half baked "sanitizing" functions which no competent developer will use.

And once again, you don't need HMAC to store salt outside the hash. You just don't. HMAC doesn't dictate where you store your salt. You're bundling these two things together as if they're inseparable, but they're two completely separate things.

in any sane situation:

1) The SQL database is not public (any more than it's common to have public anonymous FTP to your source code). It listens to a specific interface, from specific IPs with a specific user and password (which is not "root"/"").

Yes, mostly true, but in the common case passwords are shared between users, and in corporate apps there are some widely shared "admin" accounts that are user accounts with some added capabilities, or some such bad practice.

you pass it as a parameter to a prepared statement. You do this to all data, all DB layers provide escaping, and it's absolutely trivial to do. And for prepared statements, there is no way to do it wrong

Yeah, so what exactly is wrong with SQL Korma? I thought that would be one succinct and abstracted way of making sure all requests are through prepared statements.

Thanks for this clarification on HMAC - I was also failing to see and understand the relevance to passwords when investigating after watching the talk.