Here's my understanding of a salted / hashed authentication scheme:
string plaintextPass = Request.Form["password"];
string salt = UserRecordFromDb.Salt;
string toHash = salt + plaintextPass;
string hash = bcrypt(toHash);
return(hash == UserRecordFromDb.HashedPassword);
The trouble is, the algorithm adds the salt in a predictable way. If I could use a rainbow table to get the plaintext including the salt I could find and trash the salt. There are enough users that use bad passwords anyway to make the salt adding pattern easy to recognize1.
For example, if I recovered these plaintexts from my rainbow table attack:
1: passwordW0fkvE
2: secretJEn3cx
3: t4k3nVdksE12
4: purplehorse1593vE1r
#3 doesn't tell me very much. It's a good password so the salt is unrecognizable. But from #1 and #2 I can see the salt is probably 6 characters added on to the end. I can use that information for #4 to figure out the password is purplehorse15
.
1 Note: If you store the salt and the username / hashed pass in the same DB, I don't even need to guess what the salt is. It's right there in front of me. The extra step of guessing the salt is only necessary if the salt is stored in some other DB.