Question about ReplicatedStorage security and server-side behavior

I’m having a sincere doubt about how ReplicatedStorage works from the server’s point of view.

I would like to use a ModuleScript inside ReplicatedStorage that contains a table tracking which players currently have a “Block” ability active. However, I’m concerned about security — specifically, about what an exploiter could do.

My main questiion is:
Does the server kep a separate copy of ReplicatedStorage (including its ModuleScripts) where only the server can modify and read values safely? Or could an exploiter somehow modify the ModuleScript’s table directly and affect what the server sees?

I’m really confused about how this works internally.
Any explanation or best practices would be greatly appreciated!!!

1 Like

ReplicatedStorage is accessible by both server and clients, but ModuleScripts are executed in a way where only the server has control over the logic. Clients can’t modify server-side data directly, but they can communicate with the server via RemoteEvents or RemoteFunctions.
Exploiters may try to manipulate data sent to the server, so always validate the client’s requests and never trust client-side data blindly.

1 Like

Sooo an exploiter wouldn’t be able to modify the values or tables defined inside ModuleScripts? And if I validate everything sent through RemoteEvents to the server, is everything safe?
My biggest doubt is really about whether it’s possible, in any case, for an exploiter to manipulate tables and variables inside ModuleScripts in ReplicatedStorage.

1 Like

ModuleScripts inside ReplicatedStorage are not directly editable by exploiters, but they could try to exploit knowledge of the logic inside.

RemoteEvents are safe if you validate everything properly, but you still need to be careful with how you handle server-side logic to prevent abuse.

Even if an exploiter can send a valid message through RemoteEvents, they can’t directly manipulate server-side data like tables and variables inside ModuleScripts.

2 Likes

Short answer, yes

The server replicates the contents of replicated storage to every client. The module script on the client side can be manipulated by exploiters, but the version on the server cannot

1 Like

The client and server run in separate Lua contexts - similar to how contexts are different across Actors. This means that on the client, when you require a module, if you require the same module on the server, you will get a new table, not the one the client has.

FWIW it’s not even technically possible to sync two tables properly, as they reside in memory and can hold functions (functions cannot be transferred between Lua contexts; this is why you can’t send a function over a remote event).

The network acts as a barrier between the server and the client and creates some limitations. Everything you send over to the server (or the server sends to the client) is first serialized (i.e. converted into a format that is good for transmission), then sent, then received, and deserialized. This means that you aren’t really working with the original sent data, but with a copy of it, on the machine that received the data. This is true of everything sent between two computers.

This is why modifying a part on the client won’t (after 2016*) modify it on the server - only the client sees the change. The usual returned table from a ModuleScript will only be accessible in the context that required it; and if it’s passed to another context (e.g. via a RemoteEvent), it will always, 100%, undoubtedly be copied and then transferred.

Edit for clarification
Since most changes are dictated by the server, and because of the separation of contexts, this also means that a client cannot access the memory of other clients. This is quite obvious, but some people think that Client → Client modifications are still possible. They never were: if one client manipulated a property, that got synced to the server, which then sent the change to other clients. The server has always been authoritative in the Roblox model, but it used to be also quite lenient.


* - In order to have this behaviour, you (read: Roblox) deliberately have to write code to support the synchronisation. This already exists for Server → Client changes, so if a server modifies an Instance, all clients see it, but having the reverse is somewhat unusual in modern day game networking. There are a couple of exceptions - player characters are usually synched from the client to the server, because the client should be able to move its own player - otherwise, it’d be necessary to send the command to move the player to the server, then wait for the server to move the character, and then find out what the new position is. Since this costs time (a 500 ms ping means you have to wait 1 second for each movement), this clearly doesn’t work. Roblox’ FilteringEnabled prevented servers from receiving updates on Instances which players shouldn’t be controlling - e.g. recolouring half of the map :smiley:

3 Likes

Hmm, very interesting explanation, but just to clarify:
Can my server ModuleScript stored in ServerStorage use the table from a ModuleScript located in ReplicatedStorage without it being vulnerable to manipulation?
For example, if the table lists players who are currently using the block ability, would there be any problem or risk of client-side manipulation that could affect everyone?

So, for example, if I’m using a table from a ModuleScript in ReplicatedStorage through my ModuleScript in ServerStorage, and a player tries to remove themselves from the table when they shouldn’t, or tries to add themselves permanently to stay blocked while using other abilities — what would happen?
Would the server still see them in the table, or not?

the character`s CFrame and animations and unanchored parts which are owned by the client replicate from the client to the server

if the client changed a property of a module script in replicated storage, then it would only change for the client that changed it and that change wouldn’t replicate back to the server

What happens is that, an exploiter will be able to change the contents in the client sided version of the module. The server sided version of the module wont change at all, and cannot be changed, because that version of the module is running on Roblox’s servers, and not on the exploiter’s computer

The server would still see them in the table, nothing would change for the server

1 Like

ReplicatedStorage.ModuleScript

local Tbl = {}

Tbl.Days = 1

return Tbl

Executed in the server context

ServerScriptService.ScriptA

require(game:GetService("ReplicatedStorage").ModuleScript).Days += 50

ServerScriptService.ScriptB

task.wait(5)
require(game:GetService("ReplicatedStorage").ModuleScript).Days += 50

ServerScriptService.ScriptC

task.wait(10)
print ( require(game:GetService("ReplicatedStorage").ModuleScript).Days )
-- 100, because the require is shared between the three scripts

ServerScriptService.Actor.Script

print ( require(game:GetService("ReplicatedStorage").ModuleScript).Days )
-- 0, because the Actor is in a separate context

.

Meanwhile, in the client context

PlayerScripts.LocalScript

task.wait(20)
print ( require(game:GetService("ReplicatedStorage").ModuleScript).Days )
-- 0, completely unaffected by the server
1 Like

And if an exploiter did that through a LocalScript:

“require(game:GetService(“ReplicatedStorage”).ModuleScript).Days += 50”?

Why do you keep repeating the same question?

You got a long answer
A short one
And the best answer
And even a ELI5 (explainlikeimfive) answer