User Enumeration
If your login page has different error messages for unrecognized usernames and incorrect passwords, an attacker can write a script to submit usernames and test the response.
A safer approach is to return a generic error message when a login attempt fails.
If it takes longer to check a correct username and an incorrect password, a clever attacker will be able to spot the difference.
Make sure all login code-paths take about the same time on average. For instance, perform time-consuming operations like password-hashing even when you know the username is wrong.
Password reset pages are another avenue of attack. If somebody tries to reset a password for an unknown username, some sites will respond with a message indicating that the account does not exist. Try to avoid this.
If your password reset process involves sending an email, have the user enter their email address. Then send an email with a password reset link if the account exists - and a sign-up email if it's a new email address.
Same deal with registration pages. Try to avoid having your site tell people that a supplied username is already taken. If your usernames are email addresses, send a password reset email when a user absentmindedly tries to sign-up a second time.
If usernames need to be unique, but are not email addresses, protect your sign-up page with some sort of CAPTCHA. This will make it very difficult for an attacker to mine username information with a script.
If you are very security-minded, consider adding an exponential backoff after each failed login attempt, so subsequent retries take longer and longer.
Lastly, if each user is granted a unique URL (e.g. for user profile pages), make sure an attacker cannot enumerate usernames. It might seem like a good idea to differentiate responses with HTTP 404 (not found) and HTTP 403 (forbidden), but this leaks information.
Enter Your Username to Reset Your Password
Enter Your Email to Reset Your Password
Enter Your Email to Sign Up
Prove you're not a robot
Pick the bird with the biggest guns!
Exponential Backoff
login_failures = session[:login_failures] || 0

sleep(0.0001 * 2 ** login_failures)
URI Probing 403 404 403 404 403 403 404 403 403
No david...
...I see you alex. You are going on my list.
Well, that doesn't help.
1 Mississippi...
Oh, really?
One more for the list.