{"record":{"id":"2165b96a5c4b74fe","repo":"spring-projects/spring-security","slug":"invalid-log-rounds","errorCode":null,"errorMessage":"Invalid log_rounds","messagePattern":"Invalid log_rounds","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java","lineNumber":695,"sourceCode":"\t/**\n\t * Generate a salt for use with the BCrypt.hashpw() method\n\t * @param prefix the prefix value (default $2a)\n\t * @param log_rounds the log2 of the number of rounds of hashing to apply - the work\n\t * factor therefore increases as 2**log_rounds.\n\t * @param random an instance of SecureRandom to use\n\t * @return an encoded salt value\n\t * @exception IllegalArgumentException if prefix or log_rounds is invalid\n\t */\n\tpublic static String gensalt(String prefix, int log_rounds, SecureRandom random) throws IllegalArgumentException {\n\t\tStringBuilder rs = new StringBuilder();\n\t\tbyte rnd[] = new byte[BCRYPT_SALT_LEN];\n\n\t\tif (!prefix.startsWith(\"$2\")\n\t\t\t\t|| (prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y' && prefix.charAt(2) != 'b')) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid prefix\");\n\t\t}\n\t\tif (log_rounds < 4 || log_rounds > 31) {\n\t\t\tthrow new IllegalArgumentException(\"Invalid log_rounds\");\n\t\t}\n\n\t\trandom.nextBytes(rnd);\n\n\t\trs.append(\"$2\");\n\t\trs.append(prefix.charAt(2));\n\t\trs.append(\"$\");\n\t\tif (log_rounds < 10) {\n\t\t\trs.append(\"0\");\n\t\t}\n\t\trs.append(log_rounds);\n\t\trs.append(\"$\");\n\t\tencode_base64(rnd, rnd.length, rs);\n\t\treturn rs.toString();\n\t}\n\n\t/**\n\t * Generate a salt for use with the BCrypt.hashpw() method","sourceCodeStart":677,"sourceCodeEnd":713,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java#L677-L713","documentation":"BCrypt.gensalt throws this when the log_rounds (cost factor) is outside the supported range of 4 to 31. log_rounds determines the work factor as 2^rounds key-expansion iterations; values below 4 are insecure and above 31 overflow the algorithm. The library rejects such values with IllegalArgumentException.","triggerScenarios":"Calling BCrypt.gensalt(prefix, log_rounds, random) with log_rounds < 4 or > 31, e.g. gensalt(\"$2a\", 2) or gensalt(\"$2a\", 32), or passing a strength parsed from config/user input without bounds checking.","commonSituations":"Making strength configurable via properties and reading an unvalidated int; typo'd defaults like 0 or -1; copying Node.js bcryptjs cost values of 3; or attempting extremely high cost factors on modern hardware.","solutions":["Use a log_rounds value between 4 and 31 (10 is the typical default).","Clamp or validate the value: Math.max(4, Math.min(31, strength)) before calling.","Prefer BCryptPasswordEncoder, which also accepts -1 meaning 'use default 10'."],"exampleFix":"// before\nint rounds = Integer.parseInt(props.getProperty(\"bcrypt.rounds\")); // e.g. 40\nString salt = BCrypt.gensalt(\"$2a\", rounds);\n// after\nint rounds = Math.min(31, Math.max(4, Integer.parseInt(props.getProperty(\"bcrypt.rounds\"))));\nString salt = BCrypt.gensalt(\"$2a\", rounds);","handlingStrategy":"validation","validationCode":"boolean validRounds(int r) { return r >= 4 && r <= 31; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize strength selection in one validated constant/config accessor.","Clamp externalized config values to [4, 31] at load time.","Prefer the documented default (10) unless profiling justifies another value."],"tags":["bcrypt","value-out-of-range","spring-security","crypto"],"backgroundTag":"value-out-of-range","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}