Here would be a good addition to this post (I actually have an experimental plugin which I’ve slowly been developing which employs this and makes it much easier to reverse and apply and I call it “StringProtect” of which the actual product is better for most other things than this but is nonetheless relevant )
(Unrelated to StringProtect) I’d also look at a cryptographic key exchange like the Diffie Hellman Key Exchange. I implemented a remote system that uses this exchange. Here’s a very neat trick: Use this exchange to generate an identical key on both the client and the server (without transmitting it). Next, apply it to a Random.new object, and use random:NextInteger(1, 9e13)
as your key for this module! This should always generate a key that can fit in Roblox numbers without getting simplified, and the key will “roll” just like a rolling key. This is a not-so-secure way to do it, but, for remote security, this is incredible because it means an exploiter won’t be able to forge requests without trouble or it will quite literally break their game (and send garbage to your server but they can do that anyway). Here’s a very old implementation I wrote for a working Diffie Hellman Key Exchange in Roblox.
For best use, the rolling key should roll when the server sends a request on both sides, and roll when the client sends a request on both sides, and it should periodically roll on a synced timer (using os.time() for this is what I’d recommend). This means duplicated or invalid requests won’t work at all and will be garbage on the server and will desync the real client code unless the exploiter hooks into your local value and changes it every time. This requires that they listen for remotes, hooks into your callbacks, and syncs where it needs to sync. It also requires that they respect the synchronized clock rolls.
As for the concept behind StringProtect (which isn’t really in a usable state and probably won’t see a release anytime soon, it’s one of my “dream” projects)
By using StringValues in a Strings
Folder, you can remove your strings from the source, and thus, decompilation. I personally prefix strings with a nul byte and reverse their contents. This simply makes it slightly harder to debug without too much effort in the chance that an exploiter intercepts the string content (which is entirely possible and easy for them, but, this adds a decent measure against decompilation as it requires timing). The StringProtect plugin actually uses unreversed copies to access necessary functions (mainly string.reverse).
This basically just takes unique strings in the order they are defined, and (in a simplified way for understanding) does something functionally similar to strings[stringNumber] = string.reverse(Strings:GetChildren()[stringNumber][strings[valueStringId])
in the most secure way possible. It limits the number of times strings can be accessed based on content and hangs the client if the limit isn’t strictly followed. In necessary cases strings are given local variables wrapped in a do end
. This also tricks Synapse’s decompiler not to apply any actually helpful names (since they rely on string content).
As a dummed down version of this, you can do something like this for your password stuff, and, it won’t really help in this case because you’ll need to keep accessing that password:
local getPassword
do
local passwordValue = script:FindFirstChild("PasswordValueName")
local password = passwordValue.Value
-- Cleanup to help reduce leaking
passwordValue.Value = ""
passwordValue:Destroy()
passwordValue = nil
local content = api.decode(password, input)
do
local password = setmetatable({password}, {
__index = function()
-- Not a valid index, probably do something
end
-- Other stuff if you want, it'll EASILY be bypassed
})
getPassword = function()
-- Employ your checks as you will, exploiters can, again, EASILY get around them though
return password
end
end
password = nil
end