Showing revision #4e5d1296 of page security_faq


Security: Frequently Asked Questions

How are passwords stored?

Passwords are hashed using the bcrypt algorithm. It incorporates a randomly generated salt and is costly to bruteforce, making it ideal for secure password hashing.

Internally, Raddit uses Symfony's BCryptPasswordEncoder class, which in turn uses PHP's password_hash() to perform the actual hashing.

Why is there a maximum password length?

Due to a limitation of the Blowfish cipher used by bcrypt, the maximum password length is 72 bytes. This limitation hasn't stopped OpenBSD from adopting bcrypt, so it's probably good enough for us, too.

Are logins rate limited?

No. Rate limiting was planned for a while, but those plans were scrapped when we opened a Tor hidden service. Every user who accesses the site through Tor appears to have the IP address 127.0.0.1, making it impossible to identify individual troublemakers. We could rate limit non-Tor IP addresses, of course, however this would only lead to a false sense of security and doesn't accomplish much when the hidden service is wide-open for anyone who wants to try bruteforcing passwords.

To offset the lack of rate limiting, you should choose a secure password.

Is two-factor authentication available?

No. We used to have an implementation of it, but it suffered from severe technical problems.

Another attempt at implementing it will be made some time in the future. Until then, be extra sure to use a strong, unique password.

Why is there an admitted vulnerability present in the source code?

TL;DR: There is CAPTCHA now, so the highly unreliable attack described isn't even feasible any longer.

Some dork with a grudge against us found this particular piece of code in Raddle's source code and is presenting it as if it were some grand vulnerability:

// TODO - this is susceptible to timing attacks.
// TODO - send only one email with all the links.
foreach ($ur->lookUpByEmail($email) as $user) {
    $mailer->mail($user, $request);
}

So what's going on here?

Put simply, this is the code that's used for password resetting. When the user enters an email address and hits 'reset password', any user accounts with that email address are looked up in the database and stored in a list. That list is then iterated over to send reset emails for each user with the same email address. (Typically, an email address is only used with one account.)

For context, Raddle will not confirm whether or not there were any emails sent out. This is to protect user privacy and prevent third-parties from finding out if you have an account on Raddle associated with your email address. In comparison, WordPress will tell you if a reset email was sent successfully, and Facebook will even show the full name and profile pic of the registered user if you get their email address right!

The known vulnerability is that under special conditions, it potentially might be possible to tell if an email was sent or not, and thus find out that someone's email address is registered on Raddle. This is because the software must perform a few extra function calls in the event that the aforementioned list of user accounts contains at least one account. The result is that the scenario in which an email address is associated with a Raddle account takes a few microseconds longer to perform than if there were no users with that email.

Thus, an attacker with a significant amount of resources can send out thousands of password resetting requests with the email address of their target, and compare the average response time to that of requesting password resets with a bogus email address. Given enough data to weed out statistical errors, one could reach the conclusion that there is an account associated with the target email address, but with an extremely large margin of error.

Of course, by sending thousands of password requests, the inbox of the target user would become full very fast, making this attack very much detectable.

Given the improbability that someone would get into trouble for having a Raddle account, that providing an email address is optional, that this is not a feasible attack vector for extracting lists of our users, that the attack is easily detected by the target user when their inbox floods, and that the margin of error is so big as to making this attack almost useless, we decided to simply annotate the code for puristic reasons and include it, rather than trying to find a workaround for what's really a non-problem.


Source code

### How are passwords stored?

Passwords are hashed using the [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) algorithm. It incorporates a randomly generated salt and is costly to bruteforce, making it ideal for secure password hashing.

Internally, Raddit uses Symfony's [`BCryptPasswordEncoder` class](https://github.com/symfony/symfony/blob/3.3/src/Symfony/Component/Security/Core/Encoder/BCryptPasswordEncoder.php), which in turn uses PHP's [`password_hash()`](https://secure.php.net/manual/en/function.password-hash.php) to perform the actual hashing.

### Why is there a maximum password length?

Due to a limitation of the Blowfish cipher used by bcrypt, the maximum password length is 72 bytes. This limitation hasn't stopped OpenBSD from adopting bcrypt, so it's probably good enough for us, too.

### Are logins rate limited?

No. Rate limiting was planned for a while, but those plans were scrapped when we opened a Tor hidden service. Every user who accesses the site through Tor appears to have the IP address `127.0.0.1`, making it impossible to identify individual troublemakers. We could rate limit non-Tor IP addresses, of course, however this would only lead to a false sense of security and doesn't accomplish much when the hidden service is wide-open for anyone who wants to try bruteforcing passwords.

To offset the lack of rate limiting, you should choose a secure password.

### Is two-factor authentication available?

No. We used to have an implementation of it, but it suffered from severe technical problems.

Another attempt at implementing it will be made some time in the future. Until then, be extra sure to use a strong, unique password.

### Why is there [an admitted vulnerability](https://gitlab.com/edgyemma/raddit-app/blob/f1bcd0847204d3967c8db095b28536dd9a810194/src/AppBundle/Controller/ResetPasswordController.php) present in the source code?

**TL;DR:** There is CAPTCHA now, so the highly unreliable attack described isn't even feasible any longer.

Some dork with a grudge against us found this particular piece of code in Raddle's source code and is presenting it as if it were some grand vulnerability:

~~~php
// TODO - this is susceptible to timing attacks.
// TODO - send only one email with all the links.
foreach ($ur->lookUpByEmail($email) as $user) {
    $mailer->mail($user, $request);
}
~~~

So what's going on here?

Put simply, this is the code that's used for password resetting. When the user enters an email address and hits 'reset password', any user accounts with that email address are looked up in the database and stored in a list. That list is then iterated over to send reset emails for each user with the same email address. (Typically, an email address is only used with one account.)

For context, Raddle will not confirm whether or not there were any emails sent out. This is to protect user privacy and prevent third-parties from finding out if you have an account on Raddle associated with your email address. In comparison, WordPress will tell you if a reset email was sent successfully, and Facebook will even show the full name and profile pic of the registered user if you get their email address right!

The known vulnerability is that under special conditions, it potentially *might* be possible to tell if an email was sent or not, and thus find out that someone's email address is registered on Raddle. This is because the software must perform a few extra function calls in the event that the aforementioned list of user accounts contains at least one account. The result is that the scenario in which an email address is associated with a Raddle account takes **a few microseconds** longer to perform than if there were no users with that email.

Thus, an attacker with a significant amount of resources can send out thousands of password resetting requests with the email address of their target, and compare the average response time to that of requesting password resets with a bogus email address. Given enough data to weed out statistical errors, one could reach the conclusion that there *is* an account associated with the target email address, but with an extremely large margin of error.

Of course, by sending thousands of password requests, the inbox of the target user would become full very fast, making this attack very much detectable.

Given the improbability that someone would get into trouble for having a Raddle account, that providing an email address is optional, that this is not a feasible attack vector for extracting lists of our users, that the attack is easily detected by the target user when their inbox floods, and that the margin of error is so big as to making this attack almost useless, we decided to simply annotate the code for puristic reasons and include it, rather than trying to find a workaround for what's really a non-problem.