After playing around with bcrypt I see that the database stores the salt and the hashed password is prepended with the salt. I read this article on how hashing should be properly used which also recommends using salt among other methods and as far as I understand one of the main reasons for hashing a password with salt is that if someone obtains access to the database then it'll be hard to convert hashed password to real password.

But how does one get access to hashed+salted password in the first place? If they get access to the whole database then they can easily remove the salt from the hashed password (by removing fixed size string from the beginning of the hashed password) and then we get back to point zero where the hacker can use the obtained value to perform lookup via lookup tables.

Also most of the reputable websites who care about security will place a limit on how many times you can enter credentials so I don't understand how one could use brute force algorithm to crack someone's password.

Also most of the reputable websites who care about security will place a limit on how many times you can enter credentials so I don't understand how one could use brute force algorithm to crack someone's password.

Yes, this is indeed the case and good practice to prevent online guessing attacks, that is to prevent an attacker from guessing a password just from trying to log in with it. Salting doesn't play a major role in this because rate-limiting should be the constraining factor before salting and expensive hash functions are.

As you figured out, attackers usually get access to the hashes and the corresponding salts (and other required parameters) by obtaining the corresponding database table.

they can easily remove the salt from the hashed password

Technically yes, you can take the string that is the entry and just strip off the salt. But this won't get you anywhere and will in fact only make your life harder. It looks like you need some intutition on what salts are to understand why.

Let $H$ be your standard hash function. Now let $H_1$ be another hash function, $H_2$ yet another, $H_3$ another one, etc until you are at $H_{m}$ with $m$ being so large, it might as well be infinity (from a practical perspective). Now what a salt $s$ does is, it picks the index. That is e.g. $s=1$ would yield $H_1$ as the hash function to use. Now because these functions are all different, if you want to reverse them you'd essentially need a separate lookup table (be it pre-computed or "built" dynamically) for each and every function. This also means that if two users picked the same password $p$, e.g. 123456, but have different salts, they use different functions. This results (with high probability) in different hashes for both users, so you as an attacker can also not even see which users share passwords!
Of course you can't just hide the salt somewhere or throw it away, because then the legitimate server operator who needs to verify user passwords by hashing them with the salt-chosen function and comparing the result with the store value doesn't know which function to use and thus can't verify any user logins.

You mentioned bcrypt. bcrypt doesn't take any salt as an argument on initial hash generation. This is because most bcrypt implementations generate their own salt and hand it to you.

• Thank you for the amazing answer! Also the bcrypt impmentation I'm using has a separate function for creating salt and a separate function which takes salt and password as arguments (npmjs.com/package/bcrypt) – Yos May 19 '18 at 14:25

The password hash database gets leaked. (All or part of it.) One might obtain this information if...

• A discarded hard drive with remnants of the hashes is recovered.
No. They can't. Given $H(salt || password)$ you cannot efficiently compute $H(password)$. In general it isn't possible to compute a new hash $H(m_1)$ from $H(m_0)$ given the difference between $m_0$ and $m_1$ and one of the hashes but not knowledge of either $m$. This is because a hash function behaves like a random oracle.