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
    ScrambleSalt = scrambled_str

End Function

function Ceil(dividend, divider) ' for rounding up a number
    if (dividend mod divider) = 0 Then
        Ceil = dividend / divider
        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:

scrambled using the example sequence:

SHA512 Hash:

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.


But to me this seems like a great extra level of security.

I don't think this gives significant more additional security. If the attacker gets access to your scrambling code and secret he will need about the same time crack your passwords as before because this scrambling function does not increase the search space for brute-forcing. Even if only the algorithms is known but not the scrambling secret it will be not that hard to brute force the secret assuming that your password database is large and contains the typical trivial passwords. In other words: your idea relies completely on obscurity, i.e. that both the algorithm and the (short!) scrambling key is unknown to the attacker.

And, if you just want to add more obscurity by relying on server side secrets it is probably simpler to just make your pepper longer.

Established methods to store passwords instead rely on using hashing methods specifically designed to be slow in order to slow down attackers trying to brute-force captured password hashes and are thus robust even if both the hashed passwords and the hash method is known. See How to securely hash passwords? for more information.

  • I'm limited to classic ASP so I don't have access to algorithms designed specifically for password hashing such as argon2 or bcrypt, The best I have at my disposable is SHA512. I included a short scrambling key in the example, but I've tested it with scramble sequences of up to 10,000 numbers and it only took a few milliseconds longer to execute. – user2597933 Oct 15 '17 at 13:19
  • I suppose I was thinking more of an attacker gaining access to the database only rather than the website source code. – user2597933 Oct 15 '17 at 13:22
  • 4
    @user2597933: I don't think you are that much restricted as you believe. By implementing your scrambling code you've already implement your own kind of "key derivation function" (KDF), only that it lacks an important feature of other KDF: that it is designed to slow down brute-forcing even if the KDF is used. If you have a look at how PBKDF2 or scrypt actually work you will see that they are not that complex to implement as long as you have a hash function, which you do. – Steffen Ullrich Oct 15 '17 at 13:47
  • 3
    along these lines, iterating your hash (hashing the hash of the hash of the hash of...) would provide real security instead of mere obscurity. still not as as good as real pbkdf, but better than what's being proposed. – dandavis Oct 15 '17 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.