I need to generate random passwords when importing new users from an external source. I'm currently doing it by taking a random assortment of 8 lowercase/uppercase letters, and numbers. I am also using Ruby's
rand as a PRNG. Is this cryptographically secure?
First of all, you need to use a secure random number generator, like Ruby's
Second, you should choose a target security level for the generated passwords, i.e., how hard should they be to crack? Security levels are often given in bits, i.e., the base 2 logarithm of the number of distinct equiprobable passwords. In simpler words:
- A 64-bit security level means that there are 2^64 distinct passwords, and your system is just as likely to be choose one as any of the others;
- A "random assortment of 8 lowercase/uppercase letters and numbers" is 62 distinct characters, which means that choosing one such character at random provides log2(62) ≈ 6 bits of security. Choosing 8 such characters independently at random provides log2(62) * 8 ≈ 48 bits of security.
48 bits is crackable in practice for a really dedicated attacker; I would recommend about a 64-bit security level as the bare minimum for passwords, and I prefer about 80 bits for my own. With lowercase/uppercase letters + digits, you need 11 random characters for a 64-bit security level, and 13 characters gives you a 78-bit security level.
Before an edit your question asked about the effect of adding symbols to your passwords as well bumps the alphabet size to about 95 characters, and log2(95) ≈ 6.6, which brings those password sizes down by one character: 10 characters for a 64-bit password and 12 for a 79-bit password. If you're generating passwords at random, you can therefore see that it's not a huge effect.
In Ruby, you can simply use
SecureRandom.hex for random passwords that the user needs to change.
 pry(main)> require 'securerandom' => true  pry(main)> SecureRandom.hex => "c3c4fe04dcc0d388fa37fc5991423a5d"  pry(main)>
It uses the OS-provided secure random number generator, while the
rand() function is a Mersenne Twister (easy to predict once you see the output).
The default length is perfectly fine as well.
8 lowercase/uppercase letters, and numbers
This is the bare minimum of a weak password policy. Especially 8 characters are not enough.
As you are generating your passwords, why restrict yourself to passwords which are quite weak and easily bruteforced? I would suggest at least 64bits, but there is really no good reason not to go with 128/160, which would be more secure.
You will also have the problem of distributing the passwords, which will likely be insecure (probably email?).
Because of this, I would strongly suggest to make these one-time only passwords which the user must change after the next successful login (so basically it becomes a password reset token).
The way you do it is generally fine.
Make sure you use a secure random number generator. Some PRNGs create predictable output, making it possible to guess another user's password from my own.
The more possible outcomes your algorithm has, the more secure the password will be. This means that the longer the password, the more secure it is. However, a longer password will also be harder for the user to type. You use eight characters, which is a bit low. I recommend at least 10 characters.
In the same way, including punctuation characters increases the security a bit.
To make passwords easier for your users, you may want to remove characters that look alike, such as 0, o and O. There are also algorithms that create somewhat rememberable passwords, such as "NaughtyChopstick21@". However, it is often better if users use a password manager or create a memorable and secure password themselves.
Your password requirements could be better but will probably work as a temporary password. I'd be more concerned about how you are going to distribute those passwords. Probably by email? If you are that is very insecure. If you do go this route you should allow the passwords to be used only one time.
rand is not cryptographically secure. I would look into using sysrandom instead.
For randomly generated passwords there are two important measures:
- Make the entropy of the password high enough
- Make it as user friendly as possible.
You need to decide how many bits of security is necessary. 8 alphanumeric characters does not provide enough entropy, there are two ways you can improve that:
- Use a larger set of possible characters
- Use a longer password
Making the password longer is the more efficient of the two at increasing the entropy of your passwords. It is also the most user friendly approach because the more different characters you use the harder they will be to type. And even if they are not going to be typed by the user, the more different characters will also make the password harder to copy-paste and more likely to trigger interoperability problems.
I would say you have already gone too far by using all the alphanumeric characters. To make it more user friendly I would recommend sticking with only lower case letters. And to make it secure I would recommend to make the password long enough.
Assume your security goal is 64 bits, you could either go with:
- 14 random lower case characters
- 10 random characters from all printable ASCII characters
The inconvenience of having to deal with all the special characters is much worse for the user than the inconvenience of having the generated password be 4 characters longer.
With any password system and it's security you really have to consider more than just the length/randomness of the password, it's really more important to think about how they'll be attacked.
There's two ways passwords get attacked
Online brute-force. Here the attacker is attacking the user's password via your system. Unless your user has chosen a password that can be guessing before the account lockout kicks in, the attacker won't succeed in getting in. 8 generally random characters are very very unlikely to be guessed in this scenario. Important things for you to do here is ensure your system blocks attackers and try to ensure users don't choose very common passwords like 123456.
Offline brute-force Here the attacker has a copy of your password database (presumbably via a flaw such as SQL Injection, if it's a web based system) and is trying to crack the passwords. Likely the most important control here is what algorithm you're using to store the passwords. Here's I'll defer to Thomas Pornin to explain the options but it's worth noting that ruby has decent library support for bcrypt (Rails uses it for SecurePassword). Password length is also important here as is randomness, but cracking 8 char generally random passwords hashed with bcrypt and a decent work factor, is a non-trivial thing for most attackers. Without knowing your risk model I can't say it'll be fine, but I don't see it as dreadfully bad either.
On your point about cryptographically secure, as others have said ruby
SecureRandom is better than
Use five-word, randomly generated passphrases instead. You could adapt the wordlist found here:
Make them all lower case - easy to type, easy to memorize if needed.