Help with understanding ModuleScripts

I need to learn a bit more about ModuleScripts, the Info Site’s description of them is enough detail, but I have specific questions about them.

Number one being, can a exploiter potentially “abuse” them? (I have checks in my ModuleScripts for (server > client) and (client > server) data, basically standing as a cache, and wondering if an exploiter could hijack data.)

Here’s an example of what I mean, I currently have a ModuleScript standing as a Cache.
I made it so it can carry data, for when changing data when players are in-game, and saving that data when players leave, or the game does a shutdown, etc. I have functions that can get and set, but I implemented checks so that clients cannot abuse the functionality of the ModuleScripts. The checks listed are:

  1. Authorization from a script by getting the (LocalPlayer) and seeing if there’s data about them in the auth table, if they’re not there then it won’t do anything.

  2. Anti-Setting for both Cache Set Functionality, and Auth Set Functionality. Prevents clients from making changes.

  3. Prevent client from getting access to entire table unless authorized (Redirecting to number 1).

  4. If not authorized then potentially take action against said player for usage of “exploits”.

But for number 2, I have a thing where you can get data, but I don’t know if the client can make changes to the exact table too, thus making that useless.
Code example:

-- Majority of code taken out for privacy.
function m:get(player)
        return cache[player]
end
function m:set(player,val)
        if not players.LocalPlayer then
                cache[player] = val
        else
                return("restricted")
        end
end
function m:setauth(p,v)
        if not players.LocalPlayer then
                auth[p]=v
        else
                return("restricted")
        end
end

However, Server Scripts have access to them, but I just don’t know if exploiters can abuse the ModuleScript by replacing LocalPlayer to nil, thus making the checks useless.

It seems I forgot some stuff about Lua, but that’s because it’s been such a long time while working with Lua. Typical Programmer stuff I guess.


Questions, Suggestions, etc. Are accepted.

2 Likes

I found this reply on a topic about hackers stealing scripts:

Exploiters cannot access server script storage, so you can put your module scripts there to protect them.

You should use a Remote Event and pass any data you want to store in that module to a regular script, and then with the regular script in ServerScriptStorage you can send the information to the module also in server script storage.

That is not the issue.
And also, only code that it started off with will be shown, data would not be shown.
I want the client to see Data so that they could see it whenever, I only prevent them from writing to the data.

I would do this, but then it would be so tedious managing RemoteEvents and RemoteFunctions, so thats why I rolled with ModuleScripts.

Depends. It won’t break the client-server barrier unless you’re using a remote event. If your module has something that would increase the player’s data in some way but only on the server, the client could access it and increment their data on the client side, but it wouldn’t actually change the data on the server side.

If you were to use remotes, however, like invoking the server, then they could find the remote and fire it how ever they please. But this is something you’d have to create your own safety measures for.

In short, no. Modules don’t allow you to break the client-server boundary.

Since you want to check if the scripts are being called from the client or the server, you can use runService:IsServer() or runService:IsClient(). No way to bypass it AFAIK.

Basically, modules literally return a table of whatever is inside of the returned value or the returned value itself. Printing a require will just print the table of whatever is inside of the table.

Ex.

local module = {
    incrementData = function(self, player, amount)
        local value = player:FindFirstChild('Value')
        value.Value += amount
    end;
}
return module

-- Then in a localscript, this would increment their data on the client side, not the server. Server wouldn't see any changes:
local module = require(game:GetService('ReplicatedStorage').ModuleScript)

module:incrementData(game:GetService('Players').LocalPlayer, 99999999)

Then when you require the module, it will return the table with the functions inside. On the client, it won’t error but it won’t be “registered” on the server side and vice versa. If the module is required from the client, they won’t be able to access any server-only services like ServerStorage or ServerScriptService. The client can see client-sided changes but the server can’t unless you’re firing the server.

If you want 100% confidence that your scripts will not be read from the client, you could put the modules inside of ServerScriptService or ServerStorage and require them from there. You could then invoke the server to return this information as needed if you’re worried about data being hijacked or read without authorization. Clients also can’t modify the player value of remotes.

2 Likes

Thank you.

Also where did you get this knowledge of the runService? I wanna learn more if possible.

Not sure where I learned it. Just common knowledge I think haha. It helps with most runtime things like renderstepped (each time a frame is rendered), client-server boundary, things of that nature.

All functions and events of RunService can be found here.

1 Like

So in short, I don’t really need to protect my module from the client?
(removing all checks)

1 Like

Not really since they aren’t able to access DataStoreService, ServerScriptService or ServerStorage. As long as you aren’t allowing the client to modify their own data, you will be fine.

1 Like