How would I stop the client being able to send changed values to the server?

I’ve made a customizable weapon kit, however the problem is, the values can be changed on the client, and then be sent to the server.
So, if someone changed the Damage value from something other than what it is in Studio, so in this case 10, and then if someone were to change it using an exploit, to something like, 1000, then it would do 1000 damage instead of 10. How would I stop this?

Example:
In studio: Damage value is set to 10
image
image

image
The weapon does 10 damage normally.

An exploiter (or in this case, me, using the explorer tab) changes the value on their weapon to 90 damage.
image

image
The weapon does 90 damage.

I know the problem is that I’m sending the values to the server via a remote event, but I’m not sure how to fix it.

How would I stop this?
I hope this makes sense, since this is a pretty crucial security issue.

1 Like

Make a dictionary on the server for all the damage values, then on the server, instead of the client sending damage values, just check how much their weapon should deal.

1 Like

I apologize, but how would I go about doing this?
There are a lot of values, and since it’s supposed to be a kit, these weapons would obviously have different names.
image

Could you send your server and client code?

I’ve made a gun kit and it’s pretty easy, just make a RemoteEvent and find the equipped tool and check the attributes…

image
I’m probably doing something wrong, as most guns on the toolbox I’ve seen have both a client and server script in them, however mine (which I’m guessing is the issue) is just a local script that sends everything to the server.

Local script:

local Player = game:GetService("Players").LocalPlayer
local Mouse = Player:GetMouse()
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")

local Gun = script.Parent

local UserInputService = game:GetService("UserInputService")

local Remotes = game:GetService("ReplicatedStorage").Remotes
local Config = Gun:WaitForChild("Config")

local IdleAnimation = Humanoid:LoadAnimation(Config.Animations.IdleAnimation)
local DownAnimation = Humanoid:LoadAnimation(Config.Animations.DownAnimation)
local FireAnimation = Humanoid:LoadAnimation(Config.Animations.FireAnimation)
local ReloadAnimation = Humanoid:LoadAnimation(Config.Animations.ReloadAnimation)

local CanFire = true
local isDown = false
local onCooldown = false
local isHeld = false
local isEquipped
local CanReload = true

local AmmoGUI = script.Parent:WaitForChild("Ammo")
AmmoGUI.Parent = Player.PlayerGui

AmmoGUI.Ammo.Text = Config.Ammunition.Ammo.Value
AmmoGUI.Clip.Text = Config.Ammunition.Clip.Value

UserInputService.InputBegan:Connect(function(Input, IsTyping)
	Gun.Equipped:Connect(function()
		isEquipped = true

		AmmoGUI.Enabled = true

		if Humanoid.Health > 0 then
			IdleAnimation:Play()

			Mouse.Button1Down:Connect(function()
				if CanFire == true and isDown == false and isEquipped == true then
					isHeld = true

					while isHeld == true and onCooldown == false and AmmoGUI.Clip.Text > "0" do
						local MousePosition = Mouse.Hit.p

						Remotes.WeaponRemote:FireServer("Ranged", Gun, Config.Bullet.Damage.Value, MousePosition, Config.Bullet.MinSpread.Value, Config.Bullet.MaxSpread.Value, Config.Bullet.BulletsPerShot.Value, Config.Bullet.BulletColor.Value, Config.Gun.FireSound.Value, Config.Bullet.Range.Value, Config.Bullet.BulletSpeed.Value)

						FireAnimation:Play()

						AmmoGUI.Clip.Text = AmmoGUI.Clip.Text - 1

						onCooldown = true

						task.wait(Config.Gun.TimeBeforeNextShot.Value)
						onCooldown = false

					end
				end
			end)

			Mouse.Button1Up:Connect(function()
				isHeld = false

			end)

--[[			if Input.KeyCode == Enum.KeyCode.T and IsTyping == false then
				if isEquipped == true then
					if isDown == false then
						isDown = true

						IdleAnimation:Stop()
						FireAnimation:Stop()
						DownAnimation:Play()

					else
						isDown = false

						IdleAnimation:Play()
						DownAnimation:Stop()

					end
				end
			end ]]

			if Input.KeyCode == Enum.KeyCode.R and IsTyping == false then
				if isEquipped == true and CanReload == true and tonumber(AmmoGUI.Clip.Text) < Config.Ammunition.Clip.Value and tonumber(AmmoGUI.Ammo.Text) > 0 then
					CanFire = false
					CanReload = false

					ReloadAnimation:Play(0.1, Config.Gun.ReloadTime.Value)

					ReloadAnimation.Stopped:Wait()
					
					local MissingBullets

					if tonumber(AmmoGUI.Ammo.Text) < Config.Ammunition.Clip.Value then						
						MissingBullets = tonumber(AmmoGUI.Ammo.Text)
						
					else
						MissingBullets = Config.Ammunition.Clip.Value - tonumber(AmmoGUI.Clip.Text)
						
					end

					AmmoGUI.Clip.Text = tonumber(AmmoGUI.Clip.Text) + MissingBullets
					AmmoGUI.Ammo.Text = tonumber(AmmoGUI.Ammo.Text) - MissingBullets

					CanFire = true
					CanReload = true

				end
			end
		end
	end)

	Gun.Unequipped:Connect(function()
		isEquipped = false

		AmmoGUI.Enabled = false

		IdleAnimation:Stop()
		DownAnimation:Stop()
		FireAnimation:Stop()
		ReloadAnimation:Stop()

	end)
end)

Server script:

local Remotes = game:GetService("ReplicatedStorage").Remotes

Remotes.WeaponRemote.OnServerEvent:Connect(function(Player, Weapon, Gun, Damage, MousePosition, MinSpread, MaxSpread, BulletsPerShot, BulletColor, FireSound, Range, BulletSpeed)
	local Character = Player.Character
	
	local Folder = game:GetService("Workspace").Hitboxes

	if Weapon and Weapon == "Ranged" then
		local Barrel = Gun.Barrel.CFrame

		for i = 1, BulletsPerShot do
			task.spawn(function()
				local Bullet = Instance.new("Part", Folder)
				Bullet.Color = BulletColor
				Bullet.Material = Enum.Material.Neon
				Bullet.CanCollide = false
				Bullet.Massless = true
				Bullet.Size = Vector3.new(0.25,0.25,2.5)
				Bullet.Transparency = 1

				local Attachment1 = Instance.new("Attachment", Bullet)
				local Attachment2 = Instance.new("Attachment", Bullet)

				local Color = ColorSequence.new(BulletColor)

				local Tracer = Instance.new("Beam", Bullet)
				Tracer.Color = Color
				Tracer.LightEmission = 1
				Tracer.LightInfluence = 0
				Tracer.Brightness = 1
				Tracer.Texture = "rbxassetid://3085601267"
				Tracer.TextureSpeed = 0
				Tracer.Transparency = NumberSequence.new(0)
				Tracer.Attachment0 = Attachment1
				Tracer.Attachment1 = Attachment2
				Tracer.FaceCamera = true
				Tracer.Segments = 5
				Tracer.Width0 = 0.002
				Tracer.Width1 = 0.2

				Attachment1.CFrame = Bullet.CFrame * CFrame.new(0,0,2.5)
				Attachment2.CFrame = Bullet.CFrame * CFrame.new(0,0,-2.5)

				local Spread = CFrame.Angles(math.rad(math.random(MinSpread, MaxSpread)), math.rad(math.random(MinSpread, MaxSpread)), math.rad(math.random(MinSpread, MaxSpread)))

				Bullet.CFrame = CFrame.lookAt(Gun.Barrel.Position, MousePosition)

				Bullet.CFrame = Bullet.CFrame * Spread

				local Velocity = Instance.new("BodyVelocity", Bullet)
				Velocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
				Velocity.Velocity = Bullet.CFrame.LookVector * BulletSpeed

				local OnCooldown = false

				Bullet.Touched:Connect(function(Hit)
					local HitSound = Instance.new("Sound", Hit)
					HitSound.Name = "FireSound"
					HitSound.MaxDistance = 50
					HitSound.Volume = 1

					if not Hit:IsDescendantOf(Character) and Hit and Hit.Parent then
						if not Hit:IsDescendantOf(Bullet.Parent) then
							local Humanoid = Hit.Parent:FindFirstChild("Humanoid")
							if OnCooldown == false then
								OnCooldown = true

								if Humanoid then
									Humanoid:TakeDamage(Damage)	

									HitSound.SoundId = "rbxassetid://3802437361"
									HitSound.Playing = true

								else
									HitSound.SoundId = "rbxassetid://1489924400"
									HitSound.Playing = true

								end

								Bullet:Destroy()

							end
						end
					end
				end)

				task.wait(Range)
				Bullet:Destroy()

			end)
		end

		local ShotSound = Instance.new("Sound", Gun)
		ShotSound.Name = "FireSound"
		ShotSound.SoundId = "rbxassetid://"..FireSound
		ShotSound.MaxDistance = 50
		ShotSound.Volume = 1
		ShotSound.Playing = true

		Gun.Barrel.FlashBurst.Enabled = true
		Gun.Barrel.FlashSmoke.Enabled = true
		Gun.Barrel.Flash.Enabled = true
		Gun.Barrel.FlashLight.Enabled = true

		task.wait(0.05)

		Gun.Barrel.FlashBurst.Enabled = false
		Gun.Barrel.FlashSmoke.Enabled = false
		Gun.Barrel.Flash.Enabled = false
		Gun.Barrel.FlashLight.Enabled = false

		task.wait(ShotSound.TimeLength)
		ShotSound:Destroy()

	elseif Weapon and Weapon == "Melee" then
		local Hitbox = Instance.new("Part", Folder)
		Hitbox.Transparency = 1
		Hitbox.CanCollide = false
		Hitbox.Massless = true
		Hitbox.Size = Vector3.new(4,4,4)

		local Weld = Instance.new("Weld", Hitbox)
		Weld.Part0 = Hitbox
		Weld.Part1 = Character.HumanoidRootPart
		Weld.C0 = Weld.C0 * CFrame.new(0,0,2)

		local OnCooldown = false
		local didHit = false

		Hitbox.Touched:Connect(function(Hit)
			local HitSound = Instance.new("Sound", Hit)
			HitSound.Name = "HitSound"
			HitSound.MaxDistance = 50
			HitSound.Volume = 1
			HitSound.SoundId = "rbxassetid://"..FireSound

			if not Hit:IsDescendantOf(Character) and Hit and Hit.Parent then
				if not Hit:IsDescendantOf(Hitbox.Parent) then
					local Humanoid = Hit.Parent:FindFirstChild("Humanoid")
					if OnCooldown == false then
						if Humanoid then
							OnCooldown = true
							didHit = true
							
							HitSound.Playing = true
							
							Humanoid:TakeDamage(Damage)	

							Hitbox:Destroy()

						end
					end
				end
			end
		end)

		task.wait(0.25)
		if didHit == false then
			Hitbox:Destroy()

		end
	end
end)
Remotes.WeaponRemote.OnServerEvent:Connect(function(Player, Weapon, Gun, Damage, MousePosition, MinSpread, MaxSpread, BulletsPerShot, BulletColor, FireSound, Range, BulletSpeed)

These values are accessible on the server without including them as function parameters right? Any value the client changes won’t replicate to the server but your problem is that you’re passing these values as arguments (which can be changed by the client). I’d store the equipped weapon on the server and get the values corresponding to this weapon. This way you don’t have to pass all these arguments.

Change your server code to this:

local Remotes = game:GetService("ReplicatedStorage").Remotes

Remotes.WeaponRemote.OnServerEvent:Connect(function(Player, Weapon, Gun, MousePosition)
	local Character = Player.Character

	local Folder = game:GetService("Workspace").Hitboxes

	if Weapon and Weapon == "Ranged" then
		local Config = Gun.Config
		local Barrel = Gun.Barrel.CFrame
		
		local BulletConfig = Config.Bullet
		local BulletsPerShot = BulletConfig.BulletsPerShot.Value
		local BulletColor = BulletConfig.BulletColor.Value
		local MinSpread = BulletConfig.MinSpread.Value
		local MaxSpread = BulletConfig.MaxSpread.Value
		local BulletSpeed = BulletConfig.BulletSpeed.Value
		local Damage = BulletConfig.Damage.Value
		local Range = BulletConfig.Range.Value
		
		local GunConfig = Config.Gun
		local FireSound = GunConfig.FireSound.Value
		
		for i = 1, BulletsPerShot do
			task.spawn(function()
				local Bullet = Instance.new("Part")
				Bullet.Color = BulletColor
				Bullet.Material = Enum.Material.Neon
				Bullet.CanCollide = false
				Bullet.Massless = true
				Bullet.Size = Vector3.new(0.25,0.25,2.5)
				Bullet.Transparency = 1
				Bullet.Parent = Folder

				local Attachment1 = Instance.new("Attachment", Bullet)
				local Attachment2 = Instance.new("Attachment", Bullet)

				local Color = ColorSequence.new(BulletColor)

				local Tracer = Instance.new("Beam")
				Tracer.Color = Color
				Tracer.LightEmission = 1
				Tracer.LightInfluence = 0
				Tracer.Brightness = 1
				Tracer.Texture = "rbxassetid://3085601267"
				Tracer.TextureSpeed = 0
				Tracer.Transparency = NumberSequence.new(0)
				Tracer.Attachment0 = Attachment1
				Tracer.Attachment1 = Attachment2
				Tracer.FaceCamera = true
				Tracer.Segments = 5
				Tracer.Width0 = 0.002
				Tracer.Width1 = 0.2
				Tracer.Parent = Bullet

				Attachment1.CFrame = Bullet.CFrame * CFrame.new(0,0,2.5)
				Attachment2.CFrame = Bullet.CFrame * CFrame.new(0,0,-2.5)

				local Spread = CFrame.Angles(math.rad(math.random(MinSpread, MaxSpread)), math.rad(math.random(MinSpread, MaxSpread)), math.rad(math.random(MinSpread, MaxSpread)))

				Bullet.CFrame = CFrame.lookAt(Gun.Barrel.Position, MousePosition)
				Bullet.CFrame *= Spread

				local Velocity = Instance.new("BodyVelocity")
				Velocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
				Velocity.Velocity = Bullet.CFrame.LookVector * BulletSpeed
				Velocity.Parent = Bullet

				local OnCooldown = false

				Bullet.Touched:Connect(function(Hit)
					local HitSound = Instance.new("Sound", Hit)
					HitSound.Name = "FireSound"
					HitSound.MaxDistance = 50
					HitSound.Volume = 1

					if not Hit:IsDescendantOf(Character) and Hit and Hit.Parent then
						if not Hit:IsDescendantOf(Bullet.Parent) then
							local Humanoid = Hit.Parent:FindFirstChild("Humanoid")
							if OnCooldown == false then
								OnCooldown = true

								if Humanoid then
									Humanoid:TakeDamage(Damage)	

									HitSound.SoundId = "rbxassetid://3802437361"
									HitSound.Playing = true

								else
									HitSound.SoundId = "rbxassetid://1489924400"
									HitSound.Playing = true

								end

								Bullet:Destroy()

							end
						end
					end
				end)

				task.wait(Range)
				Bullet:Destroy()
			end)
		end

		local ShotSound = Instance.new("Sound", Gun)
		ShotSound.Name = "FireSound"
		ShotSound.SoundId = "rbxassetid://"..FireSound
		ShotSound.MaxDistance = 50
		ShotSound.Volume = 1
		ShotSound.Playing = true

		Gun.Barrel.FlashBurst.Enabled = true
		Gun.Barrel.FlashSmoke.Enabled = true
		Gun.Barrel.Flash.Enabled = true
		Gun.Barrel.FlashLight.Enabled = true

		task.wait(0.05)

		Gun.Barrel.FlashBurst.Enabled = false
		Gun.Barrel.FlashSmoke.Enabled = false
		Gun.Barrel.Flash.Enabled = false
		Gun.Barrel.FlashLight.Enabled = false

		task.wait(ShotSound.TimeLength)
		ShotSound:Destroy()

	elseif Weapon and Weapon == "Melee" then
		local Hitbox = Instance.new("Part", Folder)
		Hitbox.Transparency = 1
		Hitbox.CanCollide = false
		Hitbox.Massless = true
		Hitbox.Size = Vector3.new(4,4,4)

		local Weld = Instance.new("Weld", Hitbox)
		Weld.Part0 = Hitbox
		Weld.Part1 = Character.HumanoidRootPart
		Weld.C0 = Weld.C0 * CFrame.new(0,0,2)

		local OnCooldown = false
		local didHit = false

		Hitbox.Touched:Connect(function(Hit)
			local HitSound = Instance.new("Sound", Hit)
			HitSound.Name = "HitSound"
			HitSound.MaxDistance = 50
			HitSound.Volume = 1
			HitSound.SoundId = "rbxassetid://".. FireSound

			if not Hit:IsDescendantOf(Character) and Hit and Hit.Parent then
				if not Hit:IsDescendantOf(Hitbox.Parent) then
					local Humanoid = Hit.Parent:FindFirstChild("Humanoid")
					if OnCooldown == false then
						if Humanoid then
							OnCooldown = true
							didHit = true

							HitSound.Playing = true

							Humanoid:TakeDamage(Damage)	

							Hitbox:Destroy()

						end
					end
				end
			end
		end)

		task.wait(0.25)

		if not didHit then
			Hitbox:Destroy()
		end
	end
end)

And your client code to this:

local Player = game:GetService("Players").LocalPlayer
local Mouse = Player:GetMouse()
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")

local Gun = script.Parent

local UserInputService = game:GetService("UserInputService")

local Remotes = game:GetService("ReplicatedStorage").Remotes
local Config = Gun:WaitForChild("Config")

local IdleAnimation = Humanoid:LoadAnimation(Config.Animations.IdleAnimation)
local DownAnimation = Humanoid:LoadAnimation(Config.Animations.DownAnimation)
local FireAnimation = Humanoid:LoadAnimation(Config.Animations.FireAnimation)
local ReloadAnimation = Humanoid:LoadAnimation(Config.Animations.ReloadAnimation)

local CanFire = true
local isDown = false
local onCooldown = false
local isHeld = false
local isEquipped
local CanReload = true

local AmmoGUI = script.Parent:WaitForChild("Ammo")
AmmoGUI.Parent = Player.PlayerGui

AmmoGUI.Ammo.Text = Config.Ammunition.Ammo.Value
AmmoGUI.Clip.Text = Config.Ammunition.Clip.Value

UserInputService.InputBegan:Connect(function(Input, IsTyping)
	Gun.Equipped:Connect(function()
		isEquipped = true

		AmmoGUI.Enabled = true

		if Humanoid.Health > 0 then
			IdleAnimation:Play()

			Mouse.Button1Down:Connect(function()
				if CanFire == true and isDown == false and isEquipped == true then
					isHeld = true

					while isHeld == true and onCooldown == false and AmmoGUI.Clip.Text > "0" do
						local MousePosition = Mouse.Hit.p

						Remotes.WeaponRemote:FireServer("Ranged", Gun, MousePosition)

						FireAnimation:Play()

						AmmoGUI.Clip.Text = AmmoGUI.Clip.Text - 1

						onCooldown = true

						task.wait(Config.Gun.TimeBeforeNextShot.Value)
						onCooldown = false
					end
				end
			end)

			Mouse.Button1Up:Connect(function()
				isHeld = false

			end)

			--[[			
			if Input.KeyCode == Enum.KeyCode.T and IsTyping == false then
				if isEquipped == true then
					if isDown == false then
						isDown = true

						IdleAnimation:Stop()
						FireAnimation:Stop()
						DownAnimation:Play()

					else
						isDown = false

						IdleAnimation:Play()
						DownAnimation:Stop()

					end
				end
			end 
			]]

			if Input.KeyCode == Enum.KeyCode.R and not IsTyping and isEquipped and CanReload and tonumber(AmmoGUI.Clip.Text) < Config.Ammunition.Clip.Value and tonumber(AmmoGUI.Ammo.Text) > 0 then
				CanFire = false
				CanReload = false

				ReloadAnimation:Play(0.1, Config.Gun.ReloadTime.Value)

				ReloadAnimation.Stopped:Wait()

				local MissingBullets

				if tonumber(AmmoGUI.Ammo.Text) < Config.Ammunition.Clip.Value then						
					MissingBullets = tonumber(AmmoGUI.Ammo.Text)
				else
					MissingBullets = Config.Ammunition.Clip.Value - tonumber(AmmoGUI.Clip.Text)
				end

				AmmoGUI.Clip.Text = tonumber(AmmoGUI.Clip.Text) + MissingBullets
				AmmoGUI.Ammo.Text = tonumber(AmmoGUI.Ammo.Text) - MissingBullets

				CanFire = true
				CanReload = true
			end
		end
	end)

	Gun.Unequipped:Connect(function()
		isEquipped = false
		AmmoGUI.Enabled = false
		
		IdleAnimation:Stop()
		DownAnimation:Stop()
		FireAnimation:Stop()
		ReloadAnimation:Stop()
	end)
end)
4 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.