Skip to main content
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Virginia Klebau
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Virginia Klebau

RandRange() With Algorithm Argument Uses java.security.SecureRandom In Lucee CFML 5.3.7.47

By on
Tags:

Six years ago, I looked at generating "cryptographically secure" random tokens in ColdFusion by using Java's java.security.SecureRandom class. To which, Henry Ho pointed out that he had been using randRange() with the SHA1PRNG algorithm for the same purpose. After Henry's comment, I then played around with generating random bytes using randRange(); however, since I was on Adobe ColdFusion at the time, I had no idea what the actual implement was doing under-the-hood. Now that I'm on Lucee CFML, which is open-source and fully available on GitHub, I can see what is happening; and, according to the source code, the randRange() function-when used with a non-CFMX_COMPAT algorithm-uses the java.security.SecureRandom library in Lucee CFML 5.3.7.47.

Here's a snippet from the Lucee CFML source code at the time of this writing:

static Random getRandom(String algorithm, Double seed) throws ExpressionException {

	algorithm = algorithm.toLowerCase();

	Random result = randoms.get(algorithm);

	if (result == null || !seed.isNaN()) {
		if (CFMXCompat.ALGORITHM_NAME.equalsIgnoreCase(algorithm)) {

			result = new Random();
		}
		else {

			try {
				// java.security.SecureRandom imported reference.
				result = SecureRandom.getInstance(algorithm);
			}
			catch (NoSuchAlgorithmException e) {
				throw new ExpressionException("random algorithm [" + algorithm + "] is not installed on the system", e.getMessage());
			}
		}

		if (!seed.isNaN()) result.setSeed(seed.longValue());

		randoms.put(algorithm, result);
	}

	return result;
}

As you can maybe see (Java's not really my bag), when the getRandom() function is called with an algorithm other than CFMX_COMPAT, Lucee is calling:

SecureRandom.getInstance(algorithm)

Which means, when you invoke the ColdFusion function, randRange() and you pass in SHA1PRNG as the algorithm, you end up using an instance of the SecureRandom class, not the Random class (which is the default implementation).

Now, that's not to say that using randRange() with SHA1PRNG is the same as using the SecureRandom class itself. From what I can see in Lucee's source code, the instance of SecureRandom is cached the first time it is accessed; and then, used as-is going forward. Which means, it is only ever seeded once. One benefit of using the SecureRandom class directly is that you can occasionally call re-seed the secure random generator to try and break-up any pattern recognition in the output.

That said, I am not a security expert by any stretch of the imagination. As such, I don't really know how important it is to reseed the generator; especially if there's no place within your application where a malicious actor can see many sequential results of the random number output.

To make this randRange() fact a little more funzies, let's take a look at using the SHA1PRNG algorithm to generate secure, random passwords in ColdFusion. I actually wrote about random password generation in ColdFusion back in 2007 (oh my!); so, I'm gonna take that old concept and revamp it for modern Lucee CFML development.

First, I'm going to take my random password generation logic and wrap it up in a ColdFusion component so that we can reuse it. Our component is going to define set of characters such as "upper case" and "lower case" characters. We're then going to use the randRange() function to randomly select characters out of those sets.

Note that I'm extracting characters by treating strings as arrays of characters, a lovely little delighter within Lucee CFML:

component
	output = false
	hint = "I generate random passwords using the cryptographically secure SHA1PRNG algorithm."
	{

	/**
	* I initialize the password generator.
	*/
	public void function init() {

		// NOTE: I'm not using "special characters" in this demo for simplicity. Plus,
		// length is really the limiting factor when it comes to password security.
		variables.lowerCaseValues = "abcdefghijklmnopqrstuvwxyz";
		variables.upperCaseValues = lowerCaseValues.ucase();
		variables.numberValues = "0123456789";
		variables.allValues = ( lowerCaseValues & upperCaseValues & numberValues );

	}

	// ---
	// PUBLIC METHODS.
	// ---

	/**
	* I generate a secure, random password of the given minimum length.
	*/
	public string function generatePassword( numeric minLength = 15 ) {

		// Ensure that we have at least one upper, one lower, and one numeric character
		// in our password. We can then use the composite set of all characters to
		// fulfill the minimum length requirement.
		var characters = [
			randomChar( lowerCaseValues ),
			randomChar( upperCaseValues ),
			randomChar( numberValues )
		];

		while ( characters.len() < minLength ) {

			characters.append( randomChar( allValues ) );

		}

		return( characters.toList( "" ) );

	}

	// ---
	// PRIVATE METHODS.
	// ---

	/**
	* I select a random character from the given string using the SHA1PRNG algorithm for
	* cryptographically strong randomness.
	*/
	private string function randomChar( required string value ) {

		var index = randRange( 1, value.len(), "SHA1PRNG" );

		// NOTE: In Lucee CFML, you can treat a String like an Array of characters!
		return( value[ index ] );

	}

}

As you can see, our generatePassword() method starts out by selection one upper case, one lower case, and one numeric character. This is try and comply with some common "password complexity" rules. It then fleshes-out the rest of the characters using the composite set of all inputs.

Note that the selection of random characters is using the SHA1PRNG algorithm.

Now, to test this, we just instantiate it and call it a bunch of times:

<cfscript>

	passwordGenerator = new PasswordGenerator();

	loop times = 20 {

		echo( passwordGenerator.generatePassword( 20 ) & "<br />" );

	}

</cfscript>

And, when we run this ColdFusion code, we get the following randomly generated passwords:

vB313PB53v8vS1q8DUs2
mB3IHSxyV4g2z8HyR4nI
eC115ap61CFLCF4wSvb8
dR5h366tBmAF6NhnFmKC
eT5Zw9p9KFmna5S78egJ
gF50OBVilC9FaYcSdpNA
nY28MZlwDztc9ofA3Y0A
zL9B99QtDrNGjQuPLnFp
fA3BufAaiPwNyhwI3Fug
bO8EbIgOVfb9ONiVeKiB
bY2TFLQGSZiFxPXjENZ7
oC71t7le6IyjjAse3FuS
aZ8v0Q4bCOWGvwh2wOkn
gC49JYVKxtE7zOhnLHZH
dO34g9FFPevjBoGY9NxS
dA6We3lYYVmtrgR9kWnH
zY2PfmXOhyH9jdl4Hek8
aD23BJ5gupuTUsAIFRQZ
rI0mle7uBc9vd3TaWBAI
tK80ZKJMIk3kzhgI9oux

There you go - we're using the randRange() ColdFusion function with the SHA1PRNG algorithm in order to leverage the java.security.SecureRandom class for cryptographically secure random number generation which, in turn, allows us to generate secure random passwords.

Want to use code from this post? Check out the license.

Reader Comments

426 Comments

Very nice, Ben.

This would work well, as a routine to offer to users, on a sign up page.

Of course, if you are using an iPhone, it offers this service within iOS, when the password field gains focus.
But, for desktop users, you could add a link like "Generate Password" and voila it could hit an Ajax request that hits your CF routine.

Great stuff! 😀

15,688 Comments

@Charles,

I had no idea the iOS offers the option to generate passwords. Very cool! That said, I tend to create all my passwords in 1Password first, and then just copy/paste them into the form. I don't even know what 99% of my passwords are these days 😜

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel