{"record":{"id":"a38c16ffb3650d31","repo":"spring-projects/spring-security","slug":"invalid-salt-revision","errorCode":null,"errorMessage":"Invalid salt revision","messagePattern":"Invalid salt revision","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java","lineNumber":637,"sourceCode":"\t\t\tthrow new IllegalArgumentException(\"salt cannot be null\");\n\t\t}\n\n\t\tint saltLength = salt.length();\n\n\t\tif (saltLength < 28) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid salt\");\n\t\t}\n\n\t\tif (salt.charAt(0) != '$' || salt.charAt(1) != '2') {\n\t\t\tthrow new IllegalArgumentException(\"Invalid salt version\");\n\t\t}\n\t\tif (salt.charAt(2) == '$') {\n\t\t\toff = 3;\n\t\t}\n\t\telse {\n\t\t\tminor = salt.charAt(2);\n\t\t\tif ((minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b') || salt.charAt(3) != '$') {\n\t\t\t\tthrow new IllegalArgumentException(\"Invalid salt revision\");\n\t\t\t}\n\t\t\toff = 4;\n\t\t}\n\n\t\t// Extract number of rounds\n\t\tif (salt.charAt(off + 2) > '$') {\n\t\t\tthrow new IllegalArgumentException(\"Missing salt rounds\");\n\t\t}\n\n\t\tif (off == 4 && saltLength < 29) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid salt\");\n\t\t}\n\t\trounds = Integer.parseInt(salt.substring(off, off + 2));\n\n\t\treal_salt = salt.substring(off + 3, off + 25);\n\t\tsaltb = decode_base64(real_salt, BCRYPT_SALT_LEN);\n\n\t\tif (minor >= 'a') {","sourceCodeStart":619,"sourceCodeEnd":655,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java#L619-L655","documentation":"BCrypt.hashpw() parses the salt's minor revision character after the '$2' prefix (expected one of a, x, y, b followed by '$'). This IllegalArgumentException guard fires when the third character of the salt is not a recognized bcrypt revision — for example a salt of revision '$2$' without a minor letter, a corrupted hash, or a bcrypt variant unsupported by this implementation. The salt is rejected before cost-round extraction begins.","triggerScenarios":"Salt strings like $2c$..., $2z$..., $2$ with no minor, or a malformed string like $2a10$... where the '$' after the revision is missing.","commonSituations":"Hashes produced by non-OpenBSD bcrypt variants ($2y$ from PHP should be accepted; older Java ports reject it — this fork accepts a,b,x,y), hand-crafted or corrupted salt strings, string manipulation chopping a character.","solutions":["Use salts generated by BCrypt.gensalt() (defaults to a valid revision like $2a$)","Normalize known-compatible revisions ($2y$) to $2a$ before verification if interop with PHP is needed","Inspect the salt string character-by-character; it must match $2[abxy]$"],"exampleFix":"// before\nString hash = BCrypt.hashpw(pw, phpHash); // $2y$...\n// after\nString salt = phpHash.startsWith(\"$2y$\") ? \"$2a$\" + phpHash.substring(4) : phpHash;\nString hash = BCrypt.hashpw(pw, salt);","handlingStrategy":"validation","validationCode":"if (!salt.matches(\"^\\\\$2[a-z]?\\\\$\")) {\n    throw new IllegalArgumentException(\"Malformed bcrypt revision segment\");\n}","typeGuard":"boolean hasValidRevision(String s) {\n    return s.length() > 3 && \"axby\".indexOf(s.charAt(2)) >= 0 && s.charAt(3) == '$';\n}","tryCatchPattern":"try {\n    hash = BCrypt.hashpw(pw, salt);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Invalid salt revision\")) { /* log the raw salt for inspection */ }\n}","preventionTips":["Never construct salt strings by hand; use BCrypt.gensalt()","Check revision compatibility before migrating hashes between implementations"],"tags":["bcrypt","spring-security","salt","revision","format"],"backgroundTag":"invalid-argument-format","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}