expert
65 min

Anti-Exploit & Security

Protect your game from exploiters and implement security best practices.

Server ValidationSanity ChecksExploit Detection

1
Server-Side Validation

Never trust the client - always validate on the server.

Movement Validation

local MAX_SPEED = 100  -- studs per second
local lastPositions = {}

local function validateMovement(player, newPosition)
  local character = player.Character
  if not character then return false end
  
  local lastData = lastPositions[player.UserId]
  
  if lastData then
    local distance = (newPosition - lastData.position).Magnitude
    local timeDiff = tick() - lastData.time
    local speed = distance / timeDiff
    
    -- Check if speed is impossible
    if speed > MAX_SPEED then
      warn(player.Name .. " is moving too fast: " .. speed)
      return false
    end
  end
  
  lastPositions[player.UserId] = {
    position = newPosition,
    time = tick()
  }
  
  return true
end

moveRemote.OnServerEvent:Connect(function(player, position)
  if typeof(position) ~= "Vector3" then return end
  if not validateMovement(player, position) then return end
  
  -- Apply movement
  player.Character.HumanoidRootPart.Position = position
end)

Validate player movement to prevent teleportation hacks.

Purchase Validation

local ShopItems = {
  ["sword"] = {price = 100, name = "Sword"},
  ["potion"] = {price = 50, name = "Potion"}
}

purchaseRemote.OnServerEvent:Connect(function(player, itemId)
  -- Type validation
  if typeof(itemId) ~= "string" then
    warn(player.Name .. " sent invalid item ID")
    return
  end
  
  -- Item exists validation
  local item = ShopItems[itemId]
  if not item then
    warn(player.Name .. " tried to buy non-existent item")
    return
  end
  
  -- Currency validation
  local coins = player.leaderstats.Coins.Value
  if coins < item.price then
    -- Not enough money
    return
  end
  
  -- Price validation (check if price wasn't modified)
  if item.price ~= ShopItems[itemId].price then
    warn("Price mismatch detected!")
    return
  end
  
  -- All checks passed - process purchase
  player.leaderstats.Coins.Value = coins - item.price
  giveItem(player, itemId)
end)

Validate every aspect of a purchase to prevent exploits.

💡 Tips:

  • • Validate data types with typeof()
  • • Check ranges (health can't be 9999)
  • • Verify player permissions
  • • Log suspicious activity

2
Sanity Checks

Implement checks to detect impossible game states.

Value Range Checks

local function sanitizeValue(value, min, max, default)
  if typeof(value) ~= "number" then
    return default
  end
  
  if value < min or value > max then
    return default
  end
  
  return value
end

damageRemote.OnServerEvent:Connect(function(player, targetPlayer, damage)
  -- Sanitize damage value
  damage = sanitizeValue(damage, 0, 100, 0)
  
  if damage == 0 then
    warn(player.Name .. " sent invalid damage")
    return
  end
  
  -- Apply damage
  local humanoid = targetPlayer.Character:FindFirstChild("Humanoid")
  if humanoid then
    humanoid:TakeDamage(damage)
  end
end)

Sanitize all numeric values to prevent impossible values.

State Validation

local function canUseAbility(player, abilityName)
  -- Check if player is alive
  local character = player.Character
  if not character then return false end
  
  local humanoid = character:FindFirstChild("Humanoid")
  if not humanoid or humanoid.Health <= 0 then
    return false, "Player is dead"
  end
  
  -- Check if player owns the ability
  if not playerAbilities[player.UserId][abilityName] then
    warn(player.Name .. " tried to use ability they don't own")
    return false, "Ability not owned"
  end
  
  -- Check cooldown
  if isOnCooldown(player, abilityName) then
    return false, "Ability on cooldown"
  end
  
  -- Check mana/resources
  local cost = AbilityData[abilityName].cost
  if player.Stats.Mana.Value < cost then
    return false, "Not enough mana"
  end
  
  return true
end

abilityRemote.OnServerEvent:Connect(function(player, abilityName)
  local canUse, reason = canUseAbility(player, abilityName)
  
  if not canUse then
    print("Ability blocked: " .. reason)
    return
  end
  
  -- Execute ability
  executeAbility(player, abilityName)
end)

Check all prerequisites before allowing actions.

3
Exploit Detection

Detect and handle exploiters.

Anomaly Detection

local suspiciousActivity = {}

local function reportSuspiciousActivity(player, reason)
  local userId = player.UserId
  
  if not suspiciousActivity[userId] then
    suspiciousActivity[userId] = {
      count = 0,
      reasons = {}
    }
  end
  
  local activity = suspiciousActivity[userId]
  activity.count = activity.count + 1
  table.insert(activity.reasons, {
    reason = reason,
    time = tick()
  })
  
  -- Log to console
  warn(string.format("[SECURITY] %s: %s (Count: %d)", 
    player.Name, reason, activity.count))
  
  -- Automatic actions
  if activity.count >= 5 then
    -- Kick player
    player:Kick("Suspicious activity detected")
  elseif activity.count >= 3 then
    -- Warn player
    warnPlayer(player, "Stop exploiting or you will be kicked")
  end
end

-- Example usage
if not validateMovement(player, position) then
  reportSuspiciousActivity(player, "Invalid movement detected")
  return
end

Track suspicious activity and take action against repeated offenders.

💡 Tips:

  • • Log everything suspicious
  • • Don't tell exploiters what triggered detection
  • • Use progressive punishments (warn → kick → ban)
  • • Consider false positives (lag can look like exploits)

Practice Exercises

Exercise 1: Damage Validator

Create a function that validates damage values.

Starter Code:

local function validateDamage(player, damage, maxDamage)
  -- Validate the damage value
  
end
Show Solution
local function validateDamage(player, damage, maxDamage)
  -- Type check
  if typeof(damage) ~= "number" then
    warn(player.Name .. " sent non-number damage")
    return false
  end
  
  -- Range check
  if damage < 0 or damage > maxDamage then
    warn(player.Name .. " sent invalid damage: " .. damage)
    return false
  end
  
  -- Check for exact values (suspicious)
  if damage == maxDamage and math.random() < 0.01 then
    -- Very rare to hit exactly max damage every time
    reportSuspiciousActivity(player, "Always hitting max damage")
  end
  
  return true
end

Ready for more?

Continue your learning journey

Executors.Online - Your Roblox Developer Hub