Tutorial: Properly Validating User Input

Properly Validating User Input

This tutorial is aimed toward Roblox developers who have an understanding of FilteringEnabled and know the difference between the server and its clients.

Example to Work With

Let’s pretend you own a Roblox game that has a store GUI. Within the store interface, there are a variety of buttons that allow you to purchase items when you click on it. Keep this example in mind for the rest of this tutorial.

The Big Question

Here’s the big question: How do you check to see if the player has enough money for the item when they click on it?

The first logical conclusion is pretty straight-forward. Simply compare the amount of money the user has with how much the item costs. If the user has more or equal the amount of the item, allow the purchase. Otherwise, deny the purchase.

if (playerMoney >= itemPrice) then …

Now comes the next challenge. Should the check be done on the server (Script) or from the client (LocalScript)? Let’s look at the pros and cons of both, and then see what the best solution is.

Client-Side Validation

Pros:

  • It’s fast. The client can run the comparison right away.
  • Feedback. The client can inform the client on-screen about the results.

Cons:

  • Cheating and exploiting. You run the risk of having clients change local data, such as manipulating the amount of money they have in-game
  • Invisible. The server essentially has no idea that the purchase is taking place.

Server-Side Validation

Pros:

  • It’s secure. The client cannot manipulate the server’s results
  • Visible. The Server knows what the client is doing and can even inform other clients if needed.

Cons:

  • It’s slower. Latency will always be involved with communication between a client and server.

Best Practice

So…should you use client-side validation or server-side validation? The answer is both! You should use both client- and server-side validation. When you do client-side validation, you can give quick results back to the user whether or not they can make a purchase. The server will then make the final validation before actually processing the purchase.

Here’s a simple example:
When a player clicks on a button to buy an item, the client will first check to see if he or she has enough money. If not, the client will display some sort of “Insufficient Funds” message. If the client does have enough money, the client will then ask the server to make the purchase (note that the client has no authority to actually make the purchase). The server will then check the player’s money amount from a location completely cut off from the clients (as to avoid any client manipulation). If the player has enough money, the server will make the transaction. The server will then tell the client whether or not the purchase was a success. The client will then act accordingly to display the final results to the player.

Code Example

Client:

-- Client:

function BuyItem()

	 -- If not enough money:
	if (money < itemPrice) then
		-- Show insufficient funds message

	-- If enough money:
	else
		local purchaseSuccess = game.ReplicatedStorage.BuyItem:InvokeServer()
		if (purchaseSuccess) then
			-- Show success message
		else
			-- Show fail message
		end
	end

end

guiButton.MouseButton1Clicked:Connect(BuyItem)

Server:

-- Server:

function BuyItem(player)

	-- Get player's money amount from somewhere secure:
	local money = GetPlayerMoney(player)

	-- If player has enough money, make purchase and return true:
	if (money >= itemPrice) then
		SetPlayerMoney(money - itemPrice)
		return true
	end

	-- Player does not have enough money; return false:
	return false

end


game.ReplicatedStorage.BuyItem.OnServerInvoke = BuyItem

If you have any feedback/correction/suggestions on this, please let me know.

70 Likes

I agree. It’s the same thing you do when validating user inputs in websites .Not only do you add validation the the client side, you also add it to the server side. It should be the same with roblox too.

4 Likes

Exactly! That’s my main reason for writing this. Anyone developing a web app that handles such user input knows the importance of doing both validation.

3 Likes

I play a game where the server and client communicate once every few seconds. The client can purchase whatever it likes (as long as it has the funds) but the server will retroactively change what the client has if it has made an error.

2 Likes

that’s what i’ve been doing, good tutorial

1 Like

Ok after reading this I understand how functions work. Very helpful for other stuff, too, thanks.

1 Like

EDIT: These questions are for a FilteringEnabled, enabled game environment. That would’ve been nice to add on.

Say you have two ways of counting the player’s money, 1 being a regular IntValue Object:
money.Value = 100

2, being inside the client’s script:
playerVals = {money = 100}

Are they both exploitable? I’m looking for a defense-level as much as the server has, which would be a level of “the client can’t change the server’s values” (which is correct, right?)

If they’re both exploitable, shouldn’t you run a check every time a player fires a gun then, to see if they truly have enough ammo then?

And thanks for posting, this cleared up some things.

If you have FilteringEnabled checked, then yes. Otherwise the client can do whatever it wants.

2 Likes

If you have FilteringEnabled checked, then yes. Otherwise the client can do whatever it wants.[/quote]

Not true. If FilteringEnabled is not checked, the client can edit the data model. It doesn’t mean client can change server memory. I strictly recommend to store the player’s money and other crucial data for your game in server memory and not in the datamodel, FilteringEnabled or not.

The other caveat with this is that client is not able to execute server side code. This goes back to never having any “trust the client” functions in your game if necessary. There are certain exceptions like in a shooter you trust the client to say whether it hit the other person (with some checks for latency of the player and exploit checks) This is still only a bit check and not a execute whatever this client says on the server.

2 Likes

If you have FilteringEnabled checked, then yes. Otherwise the client can do whatever it wants.[/quote]

I strictly recommend to store the player’s money and other crucial data for your game in server memory and not in the datamodel, FilteringEnabled or not.

[/quote]

What do you mean by storing data in the datamodel, like an IntValue in Workspace or under a Player object would be storing in the datamodel? And storing in the server memory would be a table inside server script?

Also would really like if someone, maybe you Sorcus could answer my other question. I changed all my value containers (IntValue, NumberValue, BoolValue) to data tables inside the client script and then called ammo-hacking exploit proof.

My other question:

[spoiler] Say you have two ways of counting the player’s money, 1 being a regular IntValue Object:
money.Value = 100

2, being inside the client’s script:
playerVals = {money = 100}

Are they both exploitable? I’m looking for a defense-level as much as the server has, which would be a level of “the client can’t change the server’s values” (which is correct, right?) [/spoiler]

Not true. If FilteringEnabled is not checked, the client can edit the data model. It doesn’t mean client can change server memory.

Semantics: You have to remember that the client may always edit the datamodel, FilteringEnabled just protects you from ROBLOX automatically sending said-changes to the server to be respectfully changed.

Why you shouldn’t worry too much about validation

That being said, Sorcus is 100% right when he’s saying you shouldn’t start checking everything. Paranoia in security on ROBLOX is a downfall for you, simply because it’s impossible from our end. There’s nothing to stop players from doing the following in your game:

  • Shutting down every single server with a malformed packet
  • Falsifying RemoteEvent requests (although not from other players as far as I know).
  • Editing anything stored on the client to their liking.
  • Teleporting any items under their physics control to wherever they want
  • Messing with clientside physics in whatever way they want.
  • Spamming remote event requests to disconnect everyone.

All data is stored on the client. I don’t try to protect against everything, but I do try to do some basic input validation to help with debugging on RemoteEvents. Most of my validation is used for debugging.

By practice, it’s safer to store all your values in non-ROBLOX objects parented to the datamodel, but in the end, it doesn’t matter a whole lot. Just validate anything really important to your game and chances are no-one will bother to exploit your game.

I would always be sure to authenticate the player requesting specific commands. I know Merely has ran into issues with this before. I like to call warn() when validation fails to help with debugging.

5 Likes

Corrected the formatting of the original post. (Last post 548 days ago)

2 Likes

Necro-bumping this thread because:

  • This is still relevant in 2019, and most devs probably don’t think about this enough.
  • This is is still the simplest, most straight-forward explanation of this topic.
  • I have something cool to add after dealing with this problem for a long time!

I ran into the problem of input validation a TON when working on my game (Spell Slingers). Spell/ability casting is one of the first features I implemented; it’s mostly stayed the same because I came up with a pattern that feels very organized I figure I’d share it here. The idea here is that I had to find a way to validate many kinds of general spell inputs - like directional spells, ground-target spells, unit-target spells, and so on. I needed to provide the casting system with a general interface to work with so generation/validation can work with all kinds of data.

Disclaimer: I’m the kind of guy that puts gameplay server code in the same ModuleScript as client code.

Object oriented programming is the way to go if you want really nice clean code on Roblox. So, I started with creating an abstract class called CastType to represent a casting paradigm. This class would be used on the client to generate input data (the direction you want to cast, the unit on which you want to cast, etc) and on the server to validate input data. Here’s the functions involved:

  • CastType.new - Constructor
  • CastType:start - Called by client when the player starts the spell input (presses down the key to aim)
  • CastType:cancel - Called when the player no longer is providing spell input (releases the key or cancels the cast)
  • CastType:finish - Called right after cancel to generate cast data (direction, unit, point, etc).
  • CastType:validate - Called by the client/server to make sure the data sent makes sense, including type+sanity checks.

Then, I make a subclass of CastType in order to implement a specific kind of cast type. Let’s say you have a ground-target ability in which you pick a spot on the ground to cast the ability. For this, CastType:start summons a ring which follows your mouse until CastType:cancel is called. CastType:finish then sends the mouse’s world position.

But wait there’s more!

Practically every ability has a range limit. Having the same code for checking cast radii in multiple places violates the DRY (don’t repeat yourself) principle. I made an abstract subclass of CastType called RangeCastType for any ability that must validate against the player’s current position. In addition, abilities that are cast on Humanoids can take different forms, like allies, enemies, specific kinds of units (towers/minions). So I have another abstract subclass of RangeCastType called HumanCastType to validate humanoids as well.

This is what my class tree hierarchy looks like:

  • CastType
    • NoneCastType - Dummy cast type that never sends data (for abilities without active effects).
    • RangeCastType - Validates input data against distance from player’s character. Renders the cast range around the player’s character.
      • PointCastType - Renders an X under the mouse where the player aims, sends a Vector3.
        • ArrowCastType - For directional casting. Renders an arrow pointed towards mouse position (instead of an X).
        • GroundAoECastType - Renders an O w/ given radius (instead of an X).
        • WallCastType - Same as point, but renders a wall indicator facing the player (instead of an X).
      • HumanCastType- For spells in which a humanoid must be selected (can also filter).

Finally, I have a handful of ModuleScripts provide instances of each of these classes. This way, all abilities that share the same cast paradigm use the same instance (and therefore the same visual resources on the client) of their chosen CastType object. For HumanCastType, I pass a filter function to the constructor that determines whether the humanoid can be target (ally, enemy, tower, minion, etc).

The result: Now, everything has a single place for potentially many abilities. Sanity checks can be done on the client and the server by using the same objects, just calling different functions. Abilities can make their own subclasses of any of these if they want to “break the rules” a little bit.

Hopefully this gives you some kind of idea of how you can abstract-away the heavy lifting if you’re making a game with tons of content involving validated user input (like ability casting). I’ll talk about this specific case study from my project on my blog some more if there’s enough interest.

Download an RBXM of the above system here!*

*Note: I’m only providing this for educational purposes ONLY. This is public, client-side code used in my game, which is copyrighted.

14 Likes

I am currently making a magic-based game game, I created 5 skills, one is a simple slow projectile, one is multiple faster projectiles, one uses raycasting, one uses AoE and one is also multiple projectiles but with a different casting position.

I created a module that contains data for each skills, the skills are dictionnaries, the latest index for it is a function that fires a different function in that module which creates the attack. (For exemple a fireball.)

A problem i’m having is some skills have unique effects, which makes the skill’s function be pretty long and relies less on the skill’s data (The AoE skill is a fire tornado casted around the player that grows in size and does repeated AoE damage.)

It seems the download link will not work.

So does this mean an exploiter can change anything anywhere, client and server? Say I put values in like the ServerStorage can an exploiter access supposed values and change them freely, or is it only if u were to like store values on the client like a folder in StarterGui or something?

Assuming that there are no backdoors, exploiters won’t be able to access stuff in ServerStorage due to the FilteringEnabled rules. Same thing goes for ServerScriptService. Exploiters can, hovever, modify values in their PlayerGui, PlayerScripts, etc.

1 Like

Oh, I see so that is why they said it’s safer to store values via script than actual physical value objects as they are in risk of being manipulated through things like backdoors. Thanks for clearing that up for me and, for anyone else reading through this thread who probably had the same question.

1 Like

Very helpful tutorial! I’ve never even thought about this. I always handle things like this only on the client.

1 Like

I’ve been trying to do something similar with tweening doors, would it be beneficial to push the OpenDoor event to both the server and the client?