Skip to Content
Course content

70: SecureRandom for Secure Tokens

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've reviewed a surprising number of pull requests where developers used rand or Array#sample to generate things like password reset tokens or session IDs. On the surface, it looks fine. You get a string of characters, it looks "random" to the human eye, and it works during testing. But in a production environment, this is a critical security vulnerability.

Thinking rand is "Random Enough" for Security

Here is the fundamental problem: rand is a Pseudo-Random Number Generator (PRNG). It's deterministic. If I know the seed used to initialize the generator, or if I can observe a sequence of outputs, I can mathematically predict every single "random" value that will be generated next.

# DO NOT DO THIS for security tokens
# This is predictable if someone can guess the seed or state
token = (0...32).map { rand(36).to_s(36) }.join
puts token # "a1b2c3d4..."

If you're building a game where a monster drops a random item, rand is perfect. It's fast. But if you're generating a token that grants access to a user's account, you've just handed a roadmap to any attacker who knows how Ruby's internal Mersenne Twister algorithm works. You aren't creating randomness; you're creating a sequence that only looks random.

Using SecureRandom for Cryptographic Strength

When security is on the line, you need a CSPRNG (Cryptographically Secure Pseudo-Random Number Generator). In Ruby, that's SecureRandom. Unlike rand, SecureRandom doesn't rely on a simple mathematical formula seeded in your process; it interfaces with your operating system's entropy source (like /dev/urandom on Unix-like systems).

To use it, you first need to require the library. I usually suggest using hex or urlsafe_base64 depending on where the token is going. If it's going in a URL, urlsafe_base64 is your best friend because it avoids characters like + or / that would require annoying URL encoding.

require 'securerandom'

# Generate a 32-character hex string (16 bytes of entropy)
# Great for API keys
api_key = SecureRandom.hex(16) 
puts "API Key: #{api_key}"

# Generate a URL-safe string
# Perfect for password reset links
reset_token = SecureRandom.urlsafe_base64(32)
puts "Reset Token: #{reset_token}"

# Need a totally random UUID?
session_id = SecureRandom.uuid
puts "Session ID: #{session_id}"

The beauty of SecureRandom.urlsafe_base64 is that it's specifically designed for the use case of tokens passed in a browser address bar. I've spent way too many hours debugging "Invalid Token" errors only to realize a + character in a Base64 string was being converted to a space by the web server. urlsafe_base64 solves that for you out of the box.

One quick tip: the argument you pass to hex or urlsafe_base64 is the number of bytes of entropy, not the final length of the string. Because Base64 and Hex encoding expand the data, your resulting string will be longer than the number you passed in. If you have a specific database column limit, keep that in mind.




📋 Practical Task

Build a Secure API Key Management Utility

You are building a small internal tool to manage API keys for different service tiers. Your task is to create a class called ApiKeyGenerator that can generate different strengths of keys based on the "tier" of the user.

Implement the following requirements:

  • The class should have a method generate(tier).
  • If the tier is :basic, return a SecureRandom.hex string using 16 bytes of entropy.
  • If the tier is :premium, return a SecureRandom.urlsafe_base64 string using 32 bytes of entropy.
  • If any other tier is provided, raise an ArgumentError with the message "Unknown tier provided".

Test your implementation by generating one key for each valid tier and printing them to the console. Ensure you have required the necessary library at the top of your file.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.