How can I make non-exploitable scripts?

Hey there, I’m Drinkinix. I’ve been wondering for a while on how to make non-exploitable scripts, aka flawless ones. This means that exploiters will have a hard time exploiting your game. Where do I start?

Descriptive explanations would be very much appreciated!

– Drinkinix

6 Likes

Never, ever, ever trust the client
That’s the root of all security on Roblox, on any action check what the client sends, and make sure that it’s a valid request, and that the user is able to perform the action. Start by making your game FE compatible [don’t have a choice with that]. Use RemoteEvents/Functions. The rest kind of depends on your application.

Example:

When a remote event is fired, and it needs to start a course of action, I.e. spawning a pet, or something like that make sure the client has bought access to the said thing, then spawn it.
Sanity checks FTW.

13 Likes

Never trust the client, use modules to your benefit, and try to make everything pretty much server-sided.

For example:

I want to make a shop, well most people who aren’t very experienced would simply send the price of TextButton to the server, NO!

We want to store the prices of each item in a module, naming the TextButton exactly what it’s named in the module.

Then, we’ll only get the TextButton’s name from the client, and compare it to what’s already on the server… making it… not very possible to exploit.

11 Likes

This one I can preach. I put a vast majority of everything in the Server Script Service unless its crucial for it to be client side.

1 Like

This question has been asked hundreds of times before, please use the search feature of the forum in the future! It makes it easier for everyone

2 Likes

No code is “non-exploitable”, there’s always a way for exploiters to mess with something. They always have the upper hand with higher context levels and custom implementations allowing them to do nearly anything, little of which you can protect yourself against excluding hacky methods which are not 100% foolproof or reliable.

To mitigate the number of exploiters and limit any exploiting to fairly harmless stuff, I suggest:

  • Verifying all client input to the server (where it makes sense) to ensure that it’s a legitimate action. If I shoot my gun and it’s going in an odd direction, through buildings and hitting a player directly in the head it probably isn’t a legitimate action. Many edge cases exist so make sure you factor all those in.

  • Give the client only needed control over server actions, only allowing them to do the bare minimum. You wouldn’t give the client arbitrary code execution or the ability to change properties/create instances, so don’t give them the ability to. Ensure all potentially risky actions occur on the server with as much verification and as little client input as needed.

  • Don’t create over the top client side checks or rely on the client for exploit prevention. Any and all checks on the client can be circumvented, you shouldn’t spend precious development time creating fancy systems to obfuscate code or similar. While I do suggest implementing basic checks to decrease potential useless server traffic as possible (and just for general ease and client response time), you shouldn’t rely on them.

Of course, as with anything, this won’t apply to 100% of cases. There are an infinite number of possible game ideas and scenarios within them which not even the best tutorial or reply can account for all of. With this knowledge, you should go through your code and identify potential points of attack. You, and only you, can truly protect your code and game the best. The only things we can do is offer advice and our past experience in an attempt to educate you and other people.

5 Likes

This is trusting the client to send you the correct name of the correct TextButton, rather than some spoofed string.
What you’d want to do instead is send a reference to the TextButton to the server, and have the server collect the name

2 Likes

Several things wrong with this.

  • Not every single piece of user input needs to be verified. The name of a TextButton should be the least of your concerns.
  • Client side changes to the UI elements will not replicate to the server. If you typed in a TextBox, change a TextButton name etc. on the client, that isn’t going to replicate. Thus, it’ll return whatever the original value of it is.

Little bits of text like this don’t need to be verified in most cases, but should it need to be just send the raw input from the client (potential client side checks here are valid) and verify it on the server, don’t send references or anything like that.

2 Likes

There’s no reason to do this… like, at all.

It’s a small, simple thing. So, I can’t agree with you here.

Who cares about a spoofed string? They gain nothing from it if they don’t have the money and it’s not in the prices dictionary.

1 Like

This isn’t “every single piece”, this is the price of an item in a shop, which is probably the most used example for Roblox security. If the TextBox’s name is being compared to something on the server:

then it is very important that we verify this user input.

Think about how the client would send this information. In the original example:

-- CLIENT
--We are parsing the name of the TextButton
game:GetService("ReplicatedStorage").BuyItem:FireServer(TextButton.Name)

-- SERVER
--We are comparing this value
game:GetService("ReplicatedStorage").BuyItem.OnServerEvent:Connect(function(value)
    if value < player.Cash then
        sellItem()
    end
end)

a malicious user could simply replace the TextButton.Name with whatever they wanted to, causing unintended behaviour on the server.
The way to fix this is to send a reference to the object and then access the information from the server - all of this ignoring the fact that storing values in instance names is bad practice.

Client side checks are never valid, this is Security 101 here

That’s two too many ifs there; this is a case of trusting the client which, as anyone in security would tell you, is a bad idea.

How would there be any unintended behavior?

You put a simple sanity check, if it’s a string and if it’s in the dictionary, continue.

If it’s not, don’t. It’s as simple as that. There’s no reason to do this unless you’re just overly paranoid for whatever reason. There’s also no point in “sending a replaced version of TextButton.Name”, when, like I said, there would be sanity checks, and you’re only harming yourself if something did go wrong. What exploiter would want to do that?

3 Likes

They can’t get past the “too many ifs” though.

Do you even understand what i’m saying?

Your event example is comparing the textbutton’s name to the player’s Cash?

That’s not at all what i’m talking about, what I said is we’d have a dictionary holding the prices of each item.

The Text Button’s name would be the name of the item we’d try to find in the dictionary.

Your code, clearly… does not replicate at all what I explained to this guy. Are you confused?

1 Like

To be honest, yeah, this is a little confusing. What do you mean by “the price of TextButton”? TextButton is an instance, it doesn’t have a “price” property. You then talk about sending the Name of the TextButton, which is why I made the assumption I did.

Anyway, what I’m trying to get at is that you’re not following security conventions in your designs, and you’re encouraging some potentially problematic coding practices in your example.

If you’d really like me to explain why what you’re doing can be broken, feel free to go into more detail about how you would implement your system.


Also:

What are you storing in your dictionary - prices or item names? You’re not being clear about it at all

An example of what i’m talking about.

Say they click a text_button, that we named “Sword”.

On the server, we’d check if “Sword” (the parameter we passed, aka TextButton.Name) was in the dictionary, and then we’d check if the player’s money was >= the price of “Sword”

3 Likes

I think @xRuinedSeven meant that you would index a dictionary for the price, not get the price itself from the name:

local Prices = {
    Item1 = 5,
    Item2 = 20,
    Item3 = 30
}

buyItem.OnServerEvent:Connect(function(player, itemName)
    local price = Prices[itemName]
    if price and player.Money >= price then
        -- buy item from name
    end
end)
4 Likes

Yeah, this is exactly what I meant.

2 Likes

Your example (for whatever reason) is attempting to pass off a TextButton’s Name value as the price/cost for an item, this would never be the case. You claim this as “Security 101” despite trusting the client with providing a value for the sale of an item (no matter whether the server “gets a reference” to it or not)? That is not how it works.

If a user wanted to sell an item, you’d send the name of that item to the server and check the prices on the server, you would not send a reference to a client UI element to the server to do so. There is absolutely no harm in the client sending some modified value if the server checks it, ensures they own said item and any associated values are correct.

You’re skimming over information and not taking into account the full meaning of my post. “Potential client side checks” refer to the mere skimming over data ensuring it’s (supposedly) correct on the client before being sent to the server, overall to improve client response time and less network traffic (albeit such an amount would be negligible)

Apologies if I worded my reply incorrectly, but it seems to make sense and is legible.

1 Like

Yes this would work, but my point was that, in general, it is preferable to have input that doesn’t need to be checked than it is to check the input. This is because in more complicated systems checks become more and more complex, and there are more potential points of failure.

Whilst the example you gave is perfectly secure, it should not be entirely extrapolated from because of this.

2 Likes