Whatever message this page gives is out now! Go check it out!

Hash and verify passwords in ColdFusion

Last update:
May 18, 2026
Hash and verify passwords using Argon2, BCrypt, and SCrypt with ColdFusion's unified password hashing API.

Introduction

ColdFusion 2025.0.08 introduces a unified password hashing API that provides secure, modern password hashing using three industry-standard algorithms: Argon2, BCrypt, and SCrypt.
The API consists of two new functions:
  • PasswordHashGenerate() creates a secure hash from a plaintext password.
  • PasswordHashVerify() verifies a plaintext password against a stored hash.
Argon2 is the default algorithm. It won the Password Hashing Competition (PHC) in 2015 and is the first choice recommended by OWASP for password storage. It's defined in RFC 9106 and is compliant with data protection regulations such as GDPR, HIPAA, and PCI DSS.
The unified API replaces the need to use separate functions for each algorithm. You can switch between Argon2, BCrypt, and SCrypt by changing a single parameter, with no other code changes required.
Prerequisites
  • ColdFusion version: 2025 Update 8 (2025.0.08) or later
  • Network: No outbound network access required. Password hashing runs entirely on your ColdFusion server.

Problem statement

Storing passwords securely is a fundamental requirement for any application that manages user accounts. Despite this, many applications still rely on outdated hashing methods that are vulnerable to modern attacks.

Risks of weak password hashing

  • Brute-force attacks. General-purpose hash functions like MD5 and SHA-256 are fast by design. Attackers with GPUs or ASICs can test billions of candidate passwords per second.
  • Rainbow table attacks. Pre-computed lookup tables map common passwords to their hash values. Without unique salts, identical passwords produce identical hashes.
  • Side-channel attacks. Some algorithms leak information through timing differences, power consumption, or memory access patterns during hash computation.
  • Hardware acceleration. Algorithms that don't require significant memory are easily parallelized on GPUs, FPGAs, and custom ASICs, making brute-force attacks cheaper and faster.

Why Argon2 addresses these risks

Argon2 is a memory-hard key derivation function specifically designed for password hashing. It addresses each of the risks listed:
  • Configurable memory cost. Argon2 requires a large, tunable amount of memory during hashing. This makes GPU and ASIC attacks impractical because dedicated hardware lacks the memory capacity needed.
  • Configurable CPU cost. You control how many iterations the algorithm performs, directly increasing the time required per hash.
  • Configurable parallelism. You specify how many threads the algorithm uses, letting you balance security and performance for your server hardware.
  • Side-channel resistance. The Argon2id variant (used by ColdFusion) combines Argon2i's side-channel resistance with Argon2d's GPU resistance.
  • Automatic salt generation. Each hash includes a unique, randomly generated salt. Two hashes of the same password always produce different outputs.

Where ColdFusion fits in

Before ColdFusion 2025 Update 8, you needed separate functions for BCrypt and SCrypt, and Argon2 wasn't supported at all. The new unified API provides:
  • A single pair of functions for all three algorithms
  • Argon2 as the default, with secure out-of-the-box settings
  • The ability to switch algorithms by changing one parameter
  • Backward compatibility with hashes generated by older ColdFusion functions

Use cases

User registration
When a user creates an account, hash their password with Argon2 before storing it in the database. Never store plaintext passwords.
User login
When a user signs in, retrieve the stored hash from the database and verify the submitted password against it using PasswordHashVerify().
Password reset
When a user resets their password, generate a new Argon2 hash of the new password and replace the old hash in the database.
Algorithm migration
If your application currently uses BCrypt or SCrypt, you can gradually migrate users to Argon2. Verify the existing hash on login, then re-hash with Argon2 and store the new hash.
Regulatory compliance
Applications subject to GDPR, HIPAA, or PCI DSS requirements can use Argon2 to meet password storage standards. OWASP and NIST both recommend memory-hard algorithms for credential storage.
API token and secret hashing
In addition to user passwords, you can use PasswordHashGenerate() to hash API tokens, service account credentials, or any sensitive string that needs one-way storage.

Supported algorithms

ColdFusion 2025 Update 8 supports three password hashing algorithms. All three are available through the same PasswordHashGenerate() and PasswordHashVerify() functions.
Argon2 (recommended)
Argon2 is a memory-hard key derivation function that won the Password Hashing Competition in 2015. It's defined in RFC 9106 and is OWASP's first-choice recommendation for password hashing.
ColdFusion uses the Argon2id variant, which combines Argon2i's resistance to side-channel attacks with Argon2d's resistance to GPU-based attacks. The variant is selected automatically.
Configuration parameters:
ParameterTypeDefaultValid rangeDescription
SALTLENGTHInteger161 to 1024Length of the randomly generated salt, in bytes
HASHLENGTHInteger324 to 1024Length of the output hash, in bytes
PARALLELInteger11 to 128Number of parallel threads used during hashing
MEMORYCOSTInteger40964096 to 4 MBAmount of memory used during hashing, in kilobytes (default is approximately 64 MB)
CPUCOSTInteger31 to 1000Number of iterations (time cost)
OWASP minimum recommendations:
  • Memory: 19 MiB (19456 KB) or higher
  • Iterations: 2 or higher
  • Parallelism: 1
ColdFusion's defaults (64 MB memory, 3 iterations, parallelism of 1) exceed these minimums.
Hash output format:
$argon2id$v=19$m=65536,t=3,p=1$<base64-salt>$<base64-hash>
The hash string embeds the algorithm identifier, version, all cost parameters, the salt, and the hash value. This makes the hash self-describing, so PasswordHashVerify() can verify it without you needing to remember the original parameters.
BCrypt
BCrypt is a Blowfish-based password hashing algorithm introduced in 1999. It's widely adopted and remains an acceptable alternative when Argon2 isn't available.
Configuration parameters:
ParameterTypeDefaultValid rangeDescription
ROUNDSInteger104 to 31Cost factor. The algorithm performs 2^rounds iterations.
VERSIONString"$2a""$2a", "$2b", "$2y"BCrypt version identifier
OWASP minimum recommendation: Rounds of 10 or higher.
Limitation: BCrypt truncates passwords at 72 bytes. Characters beyond that limit aren't included in the hash.
SCrypt
SCrypt is a memory-hard key derivation function designed to resist hardware brute-force attacks. It's an acceptable alternative when Argon2 isn't available.
Configuration parameters:
ParameterTypeDefaultValid rangeDescription
SALTLENGTHInteger161 to 1024Length of the randomly generated salt, in bytes
KEYLENGTHInteger321 to 1024Length of the output key, in bytes
PARALLELInteger11 to 128Parallelization parameter (p)
MEMORYCOSTInteger8
Greater than 1, must be a power of 2, maximum 65536
1 to 256
Block size parameter (r)
CPUCOSTInteger163841-256CPU cost parameter (N)
OWASP minimum recommendations:
  • N (CPUCOST): 8 or higher
  • r (MEMORYCOST): 16384 or higher
  • p (PARALLEL): 1
Algorithm comparison
FeatureArgon2BCryptSCrypt
OWASP recommendationFirst choiceAcceptable alternativeAcceptable alternative
Memory-hardYesNoYes
Side-channel resistantYes (Argon2id)NoNo
GPU/ASIC resistantYesModerateYes
Configurable memoryYesNoYes
Password length limitNone72 bytesNone
Best forNew applicationsLegacy compatibilityResource-constrained environments

New functions

PasswordHashGenerate()
Generates a secure password hash using the specified algorithm.
Syntax:
PasswordHashGenerate(password)
PasswordHashGenerate(password, algorithm)
PasswordHashGenerate(password, algorithm, options)
Parameters:
ParameterTypeRequiredDescription
passwordStringYesThe plaintext password to hash
algorithmStringNoThe hashing algorithm: "Argon2" (default), "BCrypt", or "SCrypt"
optionsStructNoAlgorithm-specific configuration. If omitted, secure defaults are used.
Returns: A string containing the Base64-encoded hash with embedded salt and parameters.
Example:
hash = PasswordHashGenerate("mySecurePassword");
PasswordHashVerify()
Verifies a plaintext password against a stored hash.
Syntax:
PasswordHashVerify(password, hash)
PasswordHashVerify(password, hash, algorithm)
Parameters:
ParameterTypeRequiredDescription
passwordStringYesThe plaintext password to verify
hashStringYesThe stored hash to verify against
algorithmStringNoThe algorithm used to generate the hash: "Argon2", "BCrypt", or "SCrypt"
Returns: Boolean. true if the password matches the hash; false otherwise.
Example:
isValid = PasswordHashVerify("mySecurePassword", storedHash, "Argon2");

Changed and deprecated functions

ColdFusion 2025 Update 8 deprecates four older password hashing functions. They still work for backward compatibility, but you should use the new unified functions in all new code.
Deprecated functions
Deprecated functionReplacement
GenerateBCryptHash(password)PasswordHashGenerate(password, "BCrypt")
VerifyBCryptHash(password, hash)PasswordHashVerify(password, hash, "BCrypt")
GenerateSCryptHash(password)PasswordHashGenerate(password, "SCrypt")
VerifySCryptHash(password, hash)PasswordHashVerify(password, hash, "SCrypt")
Backward compatibility
Hashes generated with the deprecated functions can be verified with the new functions, and vice versa:
// Hash generated with the old function
oldHash = GenerateBCryptHash("myPassword");

// Verified with the new function
isValid = PasswordHashVerify("myPassword", oldHash, "BCrypt");
// Returns: true
// Hash generated with the new function
newHash = PasswordHashGenerate("myPassword", "BCrypt");

// Verified with the old function
isValid = VerifyBCryptHash("myPassword", newHash);
// Returns: true

Implement password hashing

Generate a hash with default settings
The simplest way to hash a password is to call PasswordHashGenerate() with just the password string. This uses Argon2 with secure defaults (64 MB memory, 3 iterations, parallelism of 1).
password = "userPassword123";
hash = PasswordHashGenerate(password);

// Example output:
// $argon2id$v=19$m=4096,t=3,p=1$cGZyNTFaNDNPaEs1TUt4Sw$CVGQlN+9KRV85Q2jOIVfmw
Generate a hash with a specific algorithm
Pass the algorithm name as the second argument:
// Argon2 (default)
argon2Hash = PasswordHashGenerate(password, "Argon2");

// BCrypt
bcryptHash = PasswordHashGenerate(password, "BCrypt");

// SCrypt
scryptHash = PasswordHashGenerate(password, "SCrypt");
Generate a hash with custom Argon2 options
For applications with specific security or performance requirements, pass a struct of options:
password = "userPassword123";
options = {
    SALTLENGTH : 16,
    HASHLENGTH : 32,
    PARALLEL   : 1,
    MEMORYCOST : 65536,
    CPUCOST    : 3
};
hash = PasswordHashGenerate(password, "Argon2", options);
Generate a hash with custom BCrypt options
password = "userPassword123";
options = {
    ROUNDS  : 12,
    VERSION : "$2a"
};
hash = PasswordHashGenerate(password, "BCrypt", options);
Generate a hash with custom SCrypt options
password = "userPassword123";
options = {
    SALTLENGTH : 16,
    KEYLENGTH  : 32,
    PARALLEL   : 1,
    MEMORYCOST : 16384,
    CPUCOST    : 8
};
hash = PasswordHashGenerate(password, "SCrypt", options);
Verify a password
To verify a password, call PasswordHashVerify() with the plaintext password and the stored hash:
password = "userPassword123";
storedHash = "$argon2id$v=19$m=65536,t=3,p=1$...";

isValid = PasswordHashVerify(password, storedHash, "Argon2");

if (isValid) {
    writeOutput("Password is correct.");
} else {
    writeOutput("Invalid password.");
}
Verify with custom options
If you generated the hash with custom options, pass the same options during verification:
options = {
    ROUNDS  : 12,
    VERSION : "$2a"
};
isValid = PasswordHashVerify(password, storedHash, "BCrypt", options);

Real-world code samples

User registration
This example hashes the user's password and stores it in the database during account creation.
<cfscript>
    submittedEmail = form.email;
    submittedPassword = form.password;

    // Hash the password with Argon2 defaults
    passwordHash = PasswordHashGenerate(submittedPassword);

    // Store the user and hash in the database
    queryExecute("
        INSERT INTO users (email, password_hash, created_at)
        VALUES (:email, :passwordHash, :createdAt)
    ", {
        email        : submittedEmail,
        passwordHash : passwordHash,
        createdAt    : now()
    });

    writeOutput("Account created successfully.");
</cfscript>
User login
This example retrieves the stored hash and verifies the submitted password.
<cfscript>
    submittedEmail = form.email;
    submittedPassword = form.password;

    // Retrieve user from the database
    qUser = queryExecute("
        SELECT user_id, email, password_hash
        FROM users
        WHERE email = :email
    ", {
        email : submittedEmail
    });

    if (qUser.recordCount eq 1) {
        // Verify the submitted password against the stored hash
        isValid = PasswordHashVerify(submittedPassword, qUser.password_hash, "Argon2");

        if (isValid) {
            session.userId = qUser.user_id;
            session.isLoggedIn = true;
            location(url="/dashboard", addtoken=false);
        } else {
            variables.errorMessage = "Invalid email or password.";
        }
    } else {
        variables.errorMessage = "Invalid email or password.";
    }
</cfscript>
Note: Note: Always return the same error message whether the email wasn't found or the password was wrong. This prevents attackers from enumerating valid email addresses.
Gradual migration from BCrypt to Argon2
If your application currently stores BCrypt hashes, you can migrate users to Argon2 transparently during login. When a user signs in, verify their existing BCrypt hash. If it's valid, re-hash with Argon2 and update the database.
<cfscript>
    submittedPassword = form.password;

    qUser = queryExecute("
        SELECT user_id, password_hash, hash_algorithm
        FROM users
        WHERE email = :email
    ", {
        email : form.email
    });

    if (qUser.recordCount eq 1) {
        currentAlgorithm = qUser.hash_algorithm;
        isValid = PasswordHashVerify(submittedPassword, qUser.password_hash, currentAlgorithm);

        if (isValid) {
            // If the user is still on BCrypt or SCrypt, upgrade to Argon2
            if (currentAlgorithm neq "Argon2") {
                newHash = PasswordHashGenerate(submittedPassword, "Argon2");
                queryExecute("
                    UPDATE users
                    SET password_hash = :newHash,
                        hash_algorithm = :algo,
                        updated_at = :updatedAt
                    WHERE user_id = :userId
                ", {
                    newHash   : newHash,
                    algo      : "Argon2",
                    updatedAt : now(),
                    userId    : qUser.user_id
                });
            }

            session.userId = qUser.user_id;
            session.isLoggedIn = true;
            location(url="/dashboard", addtoken=false);
        } else {
            variables.errorMessage = "Invalid email or password.";
        }
    } else {
        variables.errorMessage = "Invalid email or password.";
    }
</cfscript>
High-security configuration
For applications with elevated security requirements, increase the cost parameters. Higher values mean each hash takes longer to compute, which slows down brute-force attacks.
<cfscript>
    // High-security Argon2 configuration
    options = {
        MEMORYCOST : 524288,   // 512 MB of memory
        CPUCOST    : 5,        // 5 iterations
        PARALLEL   : 2         // 2 threads
    };
    hash = PasswordHashGenerate(password, "Argon2", options);
</cfscript>
<cfscript>
    // High-security BCrypt configuration
    options = {
        ROUNDS : 14   // 2^14 = 16384 iterations
    };
    hash = PasswordHashGenerate(password, "BCrypt", options);
</cfscript>
Hashing API tokens
You can use the same functions to hash API tokens or service credentials for storage.
<cfscript>
    apiToken = createUUID() & "-" & createUUID();
    tokenHash = PasswordHashGenerate(apiToken, "Argon2");

    // Store tokenHash in the database
    // When the client sends the token, verify it:
    isValid = PasswordHashVerify(submittedToken, tokenHash, "Argon2");
</cfscript>
Concurrent hashing (thread safety)
PasswordHashGenerate() and PasswordHashVerify() are thread-safe. You can call them from multiple concurrent requests without synchronization.
<cfscript>
    passwords = ["pass1", "pass2", "pass3", "pass4", "pass5"];

    for (pwd in passwords) {
        thread name="hash_#pwd#" password=pwd {
            hash = PasswordHashGenerate(attributes.password, "Argon2");
            writeLog(text="Hashed: #hash#", file="hashing");
        }
    }
</cfscript>

Migrate from older functions

Replace GenerateBCryptHash and VerifyBCryptHash
Before:
hash = GenerateBCryptHash(password);
isValid = VerifyBCryptHash(password, hash);
After:
hash = PasswordHashGenerate(password, "BCrypt");
isValid = PasswordHashVerify(password, hash, "BCrypt");
Replace GenerateSCryptHash and VerifySCryptHash
Before:
hash = GenerateSCryptHash(password);
isValid = VerifySCryptHash(password, hash);
After:
hash = PasswordHashGenerate(password, "SCrypt");
isValid = PasswordHashVerify(password, hash, "SCrypt");
Upgrade to Argon2 for new hashes
For new user registrations or password resets, switch to Argon2:
// New registrations use Argon2
hash = PasswordHashGenerate(password);
// or explicitly:
hash = PasswordHashGenerate(password, "Argon2");
Existing BCrypt and SCrypt hashes in your database remain valid. Verify them using the appropriate algorithm parameter.

Handle errors and validate parameters

Parameter type handling
The functions accept numeric values as either integers or numeric strings:
  • options.ROUNDS = 12 and options.ROUNDS = "12" are both valid.
  • options.ROUNDS = "twelve" throws an error: "The value twelve cannot be converted to a number."
  • options.ROUNDS = null uses the default value without throwing an error.
Argon2 error messages
ConditionError message
Salt length out of range"Invalid Argon2 hash format or parameters: Salt length must be >= 1 and <= 1024"
Hash length out of range"Invalid Argon2 hash format or parameters: Hash length must be >= 4 and <= 1024"
Parallelism out of range"Invalid Argon2 hash format or parameters: Parallelism must be >= 1 and <= 128"
Memory cost out of range"Invalid Argon2 hash format or parameters: Memory cost must be >= 8 and <= 4194304"
CPU cost out of range"Invalid Argon2 hash format or parameters: CPU cost must be >= 1 and <= 1000"
BCrypt error messages
ConditionError message
Rounds out of range"Invalid BCrypt hash format or parameters: Rounds must be >= 4 and <= 31"
SCrypt error messages
ConditionError message
Salt length out of range"Invalid SCrypt hash format or parameters: Salt length must be >= 1 and <= 1024"
Key length out of range"Invalid SCrypt hash format or parameters: Key length must be >= 1 and <= 1024"
Parallelism out of range"Invalid SCrypt hash format or parameters: Parallelization must be >= 1 and <= 128"
Memory cost out of range"Invalid SCrypt hash format or parameters: CPU cost must be > 1, a power of 2, and <= 65536"
CPU cost invalid"Invalid SCrypt hash format or parameters: Memory cost must be >= 1 and <= 256"
Algorithm mismatch
If you try to verify a hash with the wrong algorithm, the function returns false or throws a descriptive error, depending on the format mismatch:
// Generated with Argon2
hash = PasswordHashGenerate("password", "Argon2");

// Verified with BCrypt: returns false or throws an error
isValid = PasswordHashVerify("password", hash, "BCrypt");
Wrap hashing calls in try/catch
In production, always handle potential errors:
<cfscript>
    try {
        hash = PasswordHashGenerate(form.password, "Argon2", options);
    } catch (any e) {
        writeLog(type="error", text="Password hashing failed: #e.message#", file="security");
        variables.errorMessage = "An error occurred. Try again.";
    }
</cfscript>

Security best practices

Choose Argon2 for new applications
Argon2 is OWASP's first-choice recommendation and is resistant to GPU, ASIC, and side-channel attacks. Use it as your default for all new password hashing.
Don't lower cost parameters below OWASP minimums
The default values in ColdFusion already exceed OWASP's minimum recommendations. If you customize them, don't set them lower than:
  • Argon2: 4 MB memory, 2 iterations, parallelism of 1
  • BCrypt: 10 rounds
  • SCrypt: N of 16384, r of 8, p of 1
Never store plaintext passwords
Always hash passwords before storing them. Never log, display, or transmit plaintext passwords.
Use a unique error message
Return the same error message for both "user not found" and "wrong password" during login. This prevents attackers from enumerating valid accounts.
Hash on the server side
Always hash passwords on the server, not in the browser. Client-side hashing provides no security benefit if the hash itself becomes the credential.
Monitor hashing time
Regularly benchmark your hashing configuration. Each hash should take between 250 ms and 1 second on your production hardware. If it takes less, increase the cost parameters. If it takes more, consider reducing them or upgrading your hardware.
Protect hashes in the database
Even though password hashes are one-way, protect the database containing them:
  • Restrict database access to the minimum required accounts.
  • Encrypt the database connection.
  • Apply column-level encryption if your database supports it.
Rotate to stronger parameters over time
As hardware improves, attackers can crack hashes faster. Periodically increase your cost parameters and re-hash user passwords during their next successful login, similar to the gradual migration pattern shown earlier.

Performance considerations

Password hashing is intentionally slow. The cost parameters directly control how long each hash takes to compute. This trade-off between security and performance is by design.
Default performance characteristics
Under ColdFusion's default Argon2 settings (64 MB memory, 3 iterations, parallelism of 1), expect each hash to take roughly 200 to 500 ms, depending on your server hardware.
Tested benchmarks
During ColdFusion's internal testing, the following benchmarks were observed:
ScenarioThreshold
100 Argon2 hashes (default settings)Under 1 second total
50 BCrypt hashes (default settings)Under 1 second total
100 SCrypt hashes (default settings)Under 1 second total
30 mixed-algorithm hashesUnder 10 seconds total
Memory usage
Argon2's MEMORYCOST parameter controls how much memory each hash computation requires. With the default of 4 MB, each concurrent hashing operation allocates approximately 64 MB of memory. On a server handling many concurrent login requests, memory consumption can be significant.
To estimate peak memory usage:
Peak memory = MEMORYCOST (KB) x concurrent hashing requests
For example, with 64 MB per hash and 10 concurrent logins, you'll need approximately 640 MB of memory available for hashing alone.
Tuning for your environment
  • High-traffic applications. If your server handles thousands of logins per minute, consider lowering MEMORYCOST while increasing CPUCOST to reduce memory pressure while maintaining security.
  • High-security applications. If your server handles a moderate number of logins, increase MEMORYCOST and CPUCOST to make each hash harder to crack.
  • Resource-constrained environments. Use BCrypt or SCrypt with appropriate cost factors if your server has limited memory.
Avoid extreme parameter values
Setting MEMORYCOST to very high values (for example, 4194304 KB, or 4 GB) or CPUCOST to 1000 will cause hashing to take an extremely long time and may result in OutOfMemoryError. Test your chosen parameters under realistic load before deploying to production.

Conclusion

ColdFusion 2025 Update 8 provides a modern, unified password hashing API that makes it straightforward to adopt Argon2, the current industry standard for secure password storage. The key takeaways are:
  • Use PasswordHashGenerate() and PasswordHashVerify() for all password hashing. These two functions replace the older per-algorithm functions and support Argon2, BCrypt, and SCrypt through a single API.
  • Use Argon2 as your default. It's the OWASP first-choice recommendation, resistant to GPU and ASIC attacks, and compliant with GDPR, HIPAA, and PCI DSS.
  • Start with the defaults. ColdFusion's default Argon2 settings exceed OWASP minimums. Customize only when you have specific performance or security requirements.
  • Migrate existing hashes gradually. Verify old BCrypt or SCrypt hashes during login, then re-hash with Argon2 and update the database.
  • Treat hashing cost as a security control. Regularly benchmark and adjust your parameters as hardware improves.

Share this page

Was this page helpful?
We're glad. Tell us how this page helped.
We're sorry. Can you tell us what didn't work for you?
Thank you for your feedback. Your response will help improve this page.

On this page