How unsecured is it to place a RemoteEvent in the StarterGui folder for a voting system?

Just like the title suggests, I’ve created a voting system where a player is able to click on the physical appearance of another player with the cursor of their mouse. Every time a player is voted upon, a BillboardGui become visible above their head and shows the number of votes that player currently has.

Within my RemoteEvent, I have a ServerScript inside of it as well.

local Players = game:GetService("Players")
local RemoteEvent = script.Parent

RemoteEvent.OnServerEvent:Connect(function(player,target,Statusfolder,...)
	if player.StatusFolder.hasVoted.Value == false then
        --Loop through the players
		for _, v in ipairs(Players:GetChildren()) do

            --If target's name matches the Player's name.
			if target.Name == v.Name then
				if v.Name ~= player.Name then --This line just ensures that the player isn't voting themselves.
					if v ~= nil and v.Character.Humanoid ~= nil then
                        --Adjusts the appropriate values.
						v.StatusFolder.Votes.Value = v.StatusFolder.Votes.Value + 1
						player.StatusFolder.hasVoted.Value = true
						v.Character.Torso.Percentage_UI.Main.Vote_Count.TextLabel.Text = v.StatusFolder.Votes.Value
					end
				end
			end
		end
	end
end)

This RemoteEvent is triggered by a ModuleScript from ReplicatedStorage, and that ModuleScript is triggered from the MouseManager LocalScript within the StarterPlayerScripts.

Screen Shot 2021-01-02 at 6.25.58 PM Screen Shot 2021-01-02 at 6.27.01 PM

Screen Shot 2021-01-02 at 6.27.24 PM

Chances are you might not understand what I just said, so here’s a more simplified rundown:

So how secured is this structure? Is there a backdoor that an exploiter can find through this? Specifically, is it a bad idea to but a RemoteEvent/Script inside the StarterGui? I’m not sure what other place I can put it in.

There’s not much leeway when you’re working with UserInputService and playing with SelectionBox instances, so I thought I’d ask.

Thanks!

1 Like

You seem to pass Statusfolder and target as instances for the remote. I recommend not doing that. Instances can be spoofed with look-alike tables and that has its own issues.

The code itself doesn’t look vulnerable, though, but there are some changes I’d make.

blah_blah:Connect(function(player, targetName)
    if player.StatusFolder.hasVoted.Value then
        return
    end

    -- we avoid looping to find by just using `FindFirstChild`
    local target = Players:FindFirstChild(targetName)

    -- check that both the target is valid and not themselves
    if not target or target == player then
        return
    end

    -- check that the target has a humanoid
    -- in your check you do `v ~= nil`, which wasn't required in the loop because `v` is always something
    -- you also do `v.Character.Humanoid ~= nil`, which would error if a humanoid wasn't present
    if not target.Character or not target.Character:FindFirstChild("Humanoid") then
        return
    end

    -- first set the flag, then set the other values
    player.StatusFolder.hasVoted.Value = true
    -- Luau supports compound operators such as `+=` or `-=`
    target.StatusFolder.Votes.Value += 1
    target.Character.Torso.Percentage_UI.Main.Vote_Count.TextLabel.Text = target.StatusFolder.Votes.Value
end)

Now you may do this differently; it’s possible to merge some of the guard clauses or or invert them into a normal if statement if you want, but that’s up to your own preference. I also recommend creating a standard for naming for yourself. There is a bit of inconsistency in your instances specifically, with hasValue, Percentage_UI and Vote_Count.

7 Likes

Unaware that they were able to do that. Thanks for the heads up!

As a follow up question; even if I checked class of the instance or its existence, such as:

if not player.StatusFolder:IsA("Folder") or ~= nil then
    return
end

Could that also be spoofed no matter how much I check the instance?

Sorry I wasn’t very clear. player (the first argument) can’t be spoofed. player.StatusFolder is fine if player is the first argument that Roblox passes automatically to the remote.

You passed another argument, Statusfolder, which could be anything. It is possible to check it by:

typeof(Statusfolder) == 'Instance' and Statusfolder.ClassName == 'Folder' and Statusfolder.Parent == expected_parent

But that’s more effort than it’s worth. Since you already know the folder is located in the player instance, you can just normally index it without giving the client that control.

4 Likes