Hashes and Salts; what they are and the simple concept of it in Luau

Sorry for bumping, but, this post is largely misinformation. Cryptography is a complicated field but it is extremely important. Your implementation of a “hash function” in Lua is nothing more than a fancy random-string generator. In other words, your example was very silly and actually made no contribution to security, especially when dealing with sensitive passwords.

What you got correct is that hashing greatly differs from encryption. Hashing is a one way function (which cannot be reversed) whilst encryption is a two way function (designed to be encrypted AND decrypted). By design, hashes are designed so that the same input will always produce the same output (known as the digest). Depending on which hashing function you’re using, your digest will always be a fixed length.

Also, your description of salts is slightly off. While yes, salts are appended to the input (a password in this case) to hashed, they aren’t there to make the password itself longer. The main purpose of salts is to defend against repeating password hashes. When you add a random salt, you’re ensuring that two different users ultimately have different password hashes, even if they are using the same password. Salts also help to safeguard against password-cracking attacks using structures known as “rainbow tables”. A rainbow table is a big list of hashes from commonly used passwords (like “qwerty” and “password”).

An example of how you would actually go about creating hashes (and salts) for passwords is as follows:

  1. Prompt the player to create a password. Send this password to the server.
  2. Create some random data. This will be your salt. Append that to the password.
  3. Hash the password and salt together using a function like SHA-256 (There’s several options, just make sure you follow the guidelines set by NIST FIPS 180-4 and NIST FIPS 202.
  4. Store the UserId, hash digest, and salt together using things like DataStoreService.

An example of how you would actually use hashes after creating them is as follows:

  1. Prompt the player to enter their password. Send their response to the server.
  2. Go back to where you stored the player’s hash digest and salt.
  3. Append the salt you stored before to the password the player provided.
  4. Using your appended password + salt, run the hash function you chose before (like SHA-256).
  5. Compare the output (digest) of this with the one that matches DataStore records. If they match, you know the password is correct. Otherwise, the password provided was incorrect.

A couple things to note:

  • Stay away from things like MD5 and SHA-1. The earliest versions of SHA (which stands for Secure Hashing Algorithm) was designed by the US National Security Agency a very long time ago. These are no longer considered secure by today’s processing power.
  • There are extremely rare edge cases where two different inputs can return the same hash. This is known as a collision. Again, this is extremely rare and you don’t really need to worry about this much with modern algorithms.
  • Depending on where you are working with hashes, you don’t want to send anything out as plaintext (like the passwords to hash). This is because someone else can “sniff” that data in an attack known as a man-in-the-middle (MITM) attack. I don’t think this is a concern within Roblox, but it’s definitely important when working with websites. This is solved when you use HTTPS (originally SSL over HTTP but is usually TLS over HTTP now).

Feel free to checkout @boatbomber’s HashLib module. His implementation features over a dozen different algorithms and is really optimized.

Hopefully this was all helpful! Cryptography is a super cool field and I hope it’s something you continue to dive into.

10 Likes

See my main reply to OP. He hasn’t actually shown a hash- it’s just a random text generator.

I figured as such but I didn’t spend too long actually trying to understand the code. I thought there was something weird going on like the random generation being just for salting/etc, and as long as it was consistent throughout the game state then hashes generated during one server would work correctly.

But part of the reason why I asked about collision resistance was to catch OP in the act if they actually had 0 idea what they were doing trying to make a hash function.

Like all systems, this one has a flaw. The hashes will vary per server, so storing hashed server data would mean that each hash would have to be stored in DataStoreService for this system to work.

This was the big red flag I saw that prompted me to correct OP at 1am.

1 Like

Looking closer at the OP’s explanation this isn’t hashing at all. It’s literally a UUID storage. Whenever an object doesn’t have a UUID it generates one and caches it so that object gets the same UUID in the future.

1 Like

Yes I know. I will update with a proper bit32-orientated hashing algorithm on my SimpleBit module.

Oh, so you’re the SimpleBit dude. That explains a lot.

You keep mentioning this. SimpleBit is not a math module. The 32Bit Adder was just an addon to experiment with and misconsceptions were understood.

If you are going to keep mentioning the module, atleast look fully into it instead of just some functions in it meant for experiments.

I’d like to add out that you could just send the server the prompted password and let the server check it. This is called sanity checks and are way better.

Both of these technically aren’t correct, they can be converted back, but it would take forever. Also I’m not too experienced with hashes and stuff but your function doesn’t look like a hash but rather a random string generator? Hashes seem to use algorithms instead.

So you can’t say it’s not correct, that’s basically saying something you don’t know yourself.

The only way to ‘convert’ hashes are through brute force attacks, aka hashing every possible combination of characters and checking if the hashes match

Yes, it is more to get the basic concept that hashes are a set lenght and cannot be decoded.

And like I mentioned here, I will work on a Bit32-orientated hashing algorithm instead of a cashe-backed string assigner that the basic concept was.

Just cause I’m not experienced in one section doesn’t mean I don’t know the bare basics

Oh okay! Makes more sense now haha

Hashing is definitely a one way function and cannot be reversed. However, if you’re referring to old algorithms like MD5, those are cryptographically insecure. In contrast, modern algorithms are cryptographically secure.

If you’re one of those people who are worried about post-quantum cryptography, NSA put out some great information regarding that:

Edit: To provide some more insight into the latest public cryptanalysis: currently, MD5’s collision resistance can be broken in 2^18 time. MD5 can still be used to make sure a file hasn’t been unintentionally corrupted. SHA256 has some interesting cryptanalysis to look into, but it is still certified by NIST’s Federal Information Processing Standards.

2 Likes

Take a good read at my post. I take a deep dive in explaining hashing and other various cryptography concepts. In doing so, I give examples of how you could can create salted hashes for passwords in Roblox.

I am well aware of what sanity checks are- they’re a very simple concept that anyone with proficient knowledge on Filtering-Enabled technology would know. We’re not talking about remote event security, we’re talking about cryptography.

1 Like

I would strongly advise against promoting your own hashing algorithms- especially given the fact that you’ve branded your post as a community tutorial. It’s best to share the current industry-leading practices as opposed to your own algorithm.

Also, I would strongly advise creating your own algorithm here. For one, your previous “algorithm” was already deeply flawed. Current cryptographic hash functions have been developed by some of the best mathematicians out there, all backed by highly equipped cryptanalysis experts.

If you’re adamant on creating your own algorithm- by all means do so! It’s an incredible learning experience. But don’t advertise whatever you create as something that has been accepted by the industry.

Yo this cool thanks very much i like Cryptography! also basically a hash generator would always give the same hash if the input given was the same right? that makes me wonder how they make hash generator. I could imagine like they take account of how many characters there are, how many numbers there are, how many characters there are, how many characters is capitalised, how long is the input, is the letter after the before a vocal, is the letter before a vocal, or just combine both before and after to make a unique vocal, and that also depends on the character, and while that also take account if it was capitalize or not, or just depending on the character length, how many uppercase or so, make it combine depending on them, and just combine them so much.

uhh so basically i wanna ask if a hash generator is just them taking EVERY single thing in the input into account and combining every function in world possible every single thing like crazy, and maybe depending on the function theres a function on top of that too?

The closest I would probably be able to do is create a function to cut, shift, rotate, inverse, reverse and insert bytecode via the Bit32 module based on the characters in the input.

Right now I have very simple ‘hashing’ system that has same output is just by averageing the bytecode in a string, but that would be quite weak.

Would a combination of the utf8 & bit32 libaries be able to create a strong-enough lua based hashing algorithm or do I need more built-in libaries to accomplish this?

advise against*?

Yes, the function of a hash is to guarantee a few things:

  • One input always corresponds to the same output
  • It’s extremely difficult to find an input that corresponds to a desired output
  • Changing any tiny little bit of the input results in a completely different output