I'm updating a legacy classic ASP site and I need to store passwords, so naturally I'm being a bit over cautious about how to go about this given the limitations of Classic ASP.
I'm using both a salt and a pepper (the pepper is a constant stored on the server, not in the database), and I'm just wondering, is there any benifit to shuffling / scrambling the characters of the "pepper + password + salt" string before hashing?
I've written a function that can scramble a string of any length based on a pre-defined (secret) sequence of numbers (the sequence is also stored on the server, not in the database). So the function will always return the same scrambled value rather than a random scrambled value (which of course would be of no use).
I've been reading up a lot on password salting, but I've never seen anyone suggest that the salted password be scrambled before hashing. But to me this seems like a great extra level of security.
Just wondering what others think?
Here's the scramble function (it's written in VBscript):
Function ScrambleSalt(the_str) '// Once you've joined the pepper + password + salt, you pass it through the "ScrambleSalt" function before '// hashing. The "ScrambleSalt" function will scramble any string based on a pre-set sequence of numbers. '// The sequence is imported from a txt file (kept in an offline folder, just like the pepper). '// The sequence needs to be an incremental collection of numbers (starting from 1) but in a random order '// and comma delimited. Here's and example with 1 to 50, although the actual sequence uses 1 - 500: '// 22,8,21,45,49,42,3,36,11,47,19,9,15,23,40,16,29,31,43,38,44,4,41,13,35,26,17,14,10,28,6,5,34,12,39,1, '// 30,46,32,7,27,48,33,25,18,2,50,20,24,37 '// (^ the numbers 1 to 50 in a random order) '// How the scrambling process works (using the above example sequence) is by rearranging the characters in '// the string so that characters 22 appears first, followed by character 8, then character 21 etc, etc... '// the function cycles through the sequence ignoring any numbers that are larger than the length of the '// string until the characters in the string have all been rearranged (scrambled). '// If a string is more than 50 characters long, it will be split into individual strings, each containing '// 50 characters (or a remainder in the case of the last string). '// So if the length of the string is 120 characters long, it will be split into 3 string: '// String 1 = 50 chars (chars 1 - 50) '// String 2 = 50 chars (chars 51 - 100) '// String 3 = 20 chars (chars 101 - 120) '// Each string will be scrambled, then joined back together before being returned by the function. '// Using this method means the function can scramble strings of any length and without limit. Dim scramble_sequence, sequence_array, scramble_loop, in_loop_str, scrambled_str scramble_sequence = file_get_contents(request.ServerVariables("APPL_PHYSICAL_PATH") & "/../keys/scramble_sequence.txt") sequence_array = split(scramble_sequence,",") scramble_loop = Ceil(len(the_str),uBound(sequence_array)+1) '// round up for fx = 0 to scramble_loop-1 in_loop_str = mid(the_str,fx*(uBound(sequence_array)+1)+1,uBound(sequence_array)+1) for fy = 0 to uBound(sequence_array) if int(sequence_array(fy)) =< len(in_loop_str) then scrambled_str = scrambled_str & mid(in_loop_str,int(sequence_array(fy)),1) end if next next ScrambleSalt = scrambled_str End Function function Ceil(dividend, divider) ' for rounding up a number if (dividend mod divider) = 0 Then Ceil = dividend / divider else Ceil = Int(dividend / divider) + 1 end if End function function file_get_contents(file_path) Set fs = Server.CreateObject("Scripting.FileSystemObject") Set f = fs.OpenTextFile(file_path,1) file_get_contents = f.ReadAll f.Close : Set f = Nothing : Set fs = Nothing end function
An example of the above function in action
pepper value used for this example = "XC3Qpm7CNXauwAbX" scramble sequence used for this example = "9,39,50,43,18,11,36,7,29,41,27,34,12,45,1,14,42,13,6,4,25,19,24,33,30,20,23,10,46,16,49,38,15,5,17,8,47,28,26,3,2,40,37,44,35,32,48,22,31,21" password = "p@44w0rd" salt = "G1sWNd0andiIhOYA" concatenated pepper+password+salt: XC3Qpm7CNXauwAbXp@44w0rdG1sWNd0andiIhOYA scrambled using the example sequence: NY@aI7NsduXAwmQG4dnd4rXXObppCW13CAhia00w SHA512 Hash: 9d5a7781eeb815250c55c1a1f172c569b3b6167a48951c819e4982bea9b84bd8ecad6a417ff8f110541a1039ddf1fd8daa61a52a7c401fccae71dda77c607540
The salts are stored next to the hashed passwords, and my thinking behind this method is if a hacker is able to gain access to the database, rebuilding a rainbow table to target an individual account by implementing the salt would be almost impossible given the password+salt value is scrambled and not just concatenated, making brute-forcing the hash almost impossible (not to mention the use of the pepper too). The hacker would need access to the source code to even attempt to brute-force the hash... I also realise that concatenating a pepper+password+salt before hashing is the accepted standard, but I just thought this might be a nice extra level of security, if not a bit overkill.