Note: I'm not endorsing the client-side scrypt/bcrypt approach, but I do think it's interesting.

I'm going to refer to bcrypt because that's what your comment used, but the parent post used scrypt.

> Are you assuming that collisions against SHA256(bcrypt(p)) are harder than SHA256(p), right?

I don't think it is. I think it's assuming 2 things:

1. That SHA256 collisions are rare enough, and SHA256 attacks are hard enough, that they can be ignored.

2. That the output space of bcrypt is large enough to overcome the risk that salting is usually designed to overcome.

On the first point: This approach simply isn't worried about SHA256 collisions between passwords. Yes, it's theoretically possible that 2 users with different passwords and/or different salts might end up with the same hash. If that happened often then it would be an issue - if you had n distinct users in your system but only n/2 distinct hash values, then if an attacker had a copy of your password store, it would effectively double the pay-out each time they successfully cracked a user's password (that is, each cracked password would allow them to authenticate as 2 users).

But in practical terms, collisions are going to be tiny, and you're really worrying about the case where n distinct users have n-1 or n-2 distinct hashes. That's not going to meaningfully change your exposure.

What is more of a risk (and this might be the point you were making) is that if any of your hashes happen to collide with the hash of a known input, then you're screwed, and while that's unlikely, "unlikely" isn't an ideal protection.

When you're storing
{ SALT , HASH( SALT || INPUT) }
you're reasonably protected against such collisions because they would only be effective if the known input happened to start with the salt.

What the bcrypt approach can offer is that it knows that the input to SHA256 needs to be the output of bcrypt, so you can refuse to accept anything that isn't 186 bits long (or 31 base64 characters, depending on your approach). That constraint might well be stronger protection than a salt, although I haven't run any numbers.

Probably adding a salt is safer, and I was going to attempt serious analysis on this scheme I'd certainly want to test whether that was true or not.

On the second point, salting is usually designed to work around the scenario where 2 users have the same password and therefore (absent a salt) would have the same hash. It is not specifically intended for the scenario (described above) where two users have different passwords that happen to hash to the same thing.

In the approach discussed here, the input to the hash is the output from bcrypt. That value has already had a salt applied, so 2 users with the same original passwords would be providing different inputs to our hash function, so we would be storing different outputs.

> you're relying on the user side to properly safeguard bcrypted password

I think that's a genuine issue. You need to do a not-insignificant level of client-side processing on the user's password. You're relying on the browser disposing of that data securely. It's "just bits in RAM", but bits in RAM leak, and there's no way for the browser to know that the bits you were working with were sensitive.