Thủ thuật bảo mật và giảm thiểu lừa đảo

*Nội dung này được dịch bằng AI (Beta) và có thể có lỗi. Để xem trang này bằng tiếng Anh, hãy nhấp vào đây.

Roblox sử dụng một hệ thống vật lý phân tán được phân phối mà các khách hàng có quyền giám hộ về phân tích vật lý của các đối tượng trong điều khiển của họ, thường là nhân vật của người chơi và các đối tượng không neo ghim gần nhân vật đó.Ngoài ra, thông qua việc sử dụng phần mềm bên thứ ba, các kẻ lừa đảo có thể chạy mã Luau ngẫu nhiên trên máy khách để thao tác với mô hình dữ liệu của khách hàng và giải mã và xem mã chạy trên nó.

Tổng thể, điều này có nghĩa là một kẻ lạm dụng có kỹ năng có thể tiềm ẩn thực thi mã để gian lận trong trò chơi của bạn, bao gồm:

  • Dịch chuyển nhân vật của riêng họ xung quanh địa điểmđó.
  • Bắn không an toàn RemoteEvents hoặc kích hoạt RemoteFunctions , chẳng hạn như trao cho mình các vật phẩm mà không kiếm được chúng.
  • Chỉnh sửa nhân vật của họ WalkSpeed để di chuyển rất nhanh.

Trong khi bạn có thể triển khai các phòng thủ giới hạn để ngăn chặn các cuộc tấn công thông thường, thì rất được khuyến khích rằng bạn triển khai các chiến thuật giảm thiểu bên máy chủ bền vững hơn, vì máy chủ là quyền tối cao cho bất kỳ trải nghiệm chạy nào.

Chiến thuật thiết kế phòng thủ

Các quyết định thiết kế cơ bản có thể phục vụ như "bước đầu" các biện pháp bảo mật để ngăn chặn các lỗ hổng.Ví dụ, trong một trò chơi bắn súng mà người chơi nhận được điểm vì giết chết người chơi khác, một kẻ lừa đảo có thể tạo một nhóm các bot dịch chuyển đến cùng một nơi để chúng có thể bị giết nhanh chóng vì điểm.Với sự xâm phạm tiềm năng này, hãy xem xét hai phương pháp và kết quả dự đoán của chúng:

Tiếp cậnKết quả có thể dự đoán
Theo dõi các bots bằng cách viết mã cố gắng phát hiện chúng.
Giảm hoặc loại bỏ hoàn toàn lợi thế điểm cho lượt giết trên những người chơi mới được tạo ra.

Trong khi thiết kế phòng thủ rõ ràng không phải là giải pháp hoàn hảo hoặc toàn diện, nó có thể đóng góp vào một tiếp cận bảo mật rộng hơn, cùng với giảm thiểu bên máy chủ.

Giảm thiểu bên máy chủ

Càng nhiều càng tốt, máy chủ nên đưa ra phán quyết cuối cùng về những gì là "đúng" và tình trạng hiện tại của thế giới là gì.Khách hàng có thể, tất nhiên, yêu cầu máy chủ thực hiện các thay đổi hoặc thực hiện một hành động, nhưng máy chủ nên xác minh và phê duyệt mỗi thay đổi/hành động này trước khi kết quả được sao lưu cho các người chơi khác.

Với ngoại lệ của một số hoạt động vật lý nhất định, các thay đổi đối với mô hình dữ liệu trên khách không được sao chép lên máy chủ, do đó con đường tấn công chính thường là thông qua các sự kiện mạng mà bạn đã tuyên bố với RemoteEventsRemoteFunctions .Hãy nhớ rằng một kẻ tấn công chạy mã của riêng họ trên khách hàng của bạn có thể gọi chúng với bất kỳ dữ liệu nào họ muốn.

Xác minh loại thời gian chạy từ xa

Một con đường tấn công là cho một kẻ tấn công gọi RemoteEventsRemoteFunctions với các tham số của đánh máysai.Trong một số trường hợp, điều này có thể gây ra lỗi mã trên máy chủ lắng nghe các điều khiển này theo cách có lợi cho kẻ tấn công.

Khi sử dụng sự kiện/chức năng từ xa, bạn có thể ngăn chặn loại tấn công này bằng cách xác minh các loại kiểu truyền được trên máy chủ.Mô-đun "t" , có sẵn ở đây, hữu ích cho kiểm tra loại theo cách này.Ví dụ, cho rằng mã của mô-đun tồn tại như một ModuleScript có tên là t bên trong ReplicatedStorage :

Script địa phương trong StarterPlayerScripts

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
-- Truyền màu phần và vị trí khi gọi chức năng
local newPart = remoteFunction:InvokeServer(Color3.fromRGB(200, 0, 50), Vector3.new(0, 25, 0))
if newPart then
print("The server created the requested part:", newPart)
elseif newPart == false then
print("The server denied the request. No part was created.")
end
Tập lệnh trong ServerScriptService

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
local t = require(ReplicatedStorage:WaitForChild("t"))
-- Tạo validator kiểu trước để tránh lãng phí không cần thiết
local createPartTypeValidator = t.tuple(t.instanceIsA("Player"), t.Color3, t.Vector3)
-- Tạo phần mới với các thuộc tính đã truyền
local function createPart(player, partColor, partPosition)
-- Kiểm tra loại các tham số đã truyền
if not createPartTypeValidator(player, partColor, partPosition) then
-- Im lặng trả về "false" nếu kiểm tra loại thất bại ở đây
-- Nâng một lỗi mà không có thời gian chờ có thể bị lạm dụng để làm chậm máy chủ
-- Cung cấp phản hồi khách hàng thay vào đó!
return false
end
print(player.Name .. " requested a new part")
local newPart = Instance.new("Part")
newPart.Color = partColor
newPart.Position = partPosition
newPart.Parent = Workspace
return newPart
end
-- Gắn "createPart()" vào callback của chức năng từ xa
remoteFunction.OnServerInvoke = createPart

Xác nhận dữ liệu

Một cuộc tấn công khác mà các kẻ tấn công có thể phát động là gửi các loại hợp lệ về mặt kỹ thuật nhưng làm cho chúng cực kỳ lớn, dài hoặc khác hình thành.Ví dụ, nếu máy chủ phải thực hiện một hoạt động tốn kém trên một chuỗi mở rộng theo chiều dài, một kẻ lừa đảo có thể gửi một chuỗi vô cùng lớn hoặc bị hỏng để làm chậm máy chủ.

Tương tự, cả infNaN sẽ type() như number , nhưng cả hai có thể gây ra vấn đề lớn nếu một kẻ tấn công gửi chúng và chúng không được xử lý đúng đắn thông qua các chức năng như theo dõi:


local function isNaN(n: number): boolean
-- NaN không bao giờ bằng nhau
return n ~= n
end
local function isInf(n: number): boolean
-- Số có thể là -inf hoặc inf
return math.abs(n) == math.huge
end

Một cuộc tấn công phổ biến khác mà các kẻ tấn công có thể sử dụng là gửi tables vào chỗ của Instance .Các tải hàng phức tạp có thể mô phỏng những gì sẽ là một tham chiếu đối tượng thông thường khác.

Ví dụ, được cung cấp với một hệ thống cửa hàng trong kinh nghiệm nơi dữ liệu vật phẩm như giá được lưu trong NumberValue đối tượng, một kẻ tấn công có thể vượt qua tất cả các kiểm tra khác bằng cách thực hiện những theo dõi:

Script địa phương trong StarterPlayerScripts

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")
local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")
local payload = {
Name = "Ultra Blade",
ClassName = "Folder",
Parent = itemDataFolder,
Price = {
Name = "Price",
ClassName = "NumberValue",
Value = 0, -- Các giá trị âm cũng có thể được sử dụng, dẫn đến việc cung cấp tiền tệ thay vì lấy nó!
},
}
-- Gửi payload độc hại lên máy chủ (nó sẽ bị từ chối)
print(buyItemEvent:InvokeServer(payload)) -- Xuất "false Invalid item provided"
-- Gửi một mục thực sự lên máy chủ (điều này sẽ đi qua!)
print(buyItemEvent:InvokeServer(itemDatafolder["Real Blade"])) -- Outputs "true" and remaining currency if purchase succeeds
Tập lệnh trong ServerScriptService

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")
local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")
local function buyItem(player, item)
-- Kiểm tra xem mục đã truyền không bị giả mạo và nằm trong thư mục ItemData
if typeof(item) ~= "Instance" or not item:IsDescendantOf(itemDataFolder) then
return false, "Invalid item provided"
end
-- Máy chủ có thể tiếp tục xử lý việc mua hàng dựa trên dòng chảy ví dụ bên dưới
end
-- Gắn "buyItem()" vào callback của chức năng từ xa
buyItemEvent.OnServerInvoke = buyItem

Xác nhận giá trị

Ngoài việc xác minh các loại hợp lệdữ liệu , bạn nên xác minh các giá trị được truyền qua RemoteEventsRemoteFunctions , đảm bảo chúng hợp lệ và logic trong ngữ cảnh được yêu cầu.Hai ví dụ phổ biến là một cửa hàng trong kinh nghiệm và một hệ thống nhắm mục tiêu vũ khí.

Cửa hàng trong kinh chọn mua

Hãy xem xét một hệ thống cửa hàng trong kinh nghiệm với giao diện người dùng, ví dụ như một menu lựa chọn sản phẩm với nút "Mua".Khi nút được nhấn, bạn có thể kích hoạt một RemoteFunction giữa khách hàng và máy chủ để yêu cầu mua hàng.Tuy nhiên, quan trọng là máy chủ server , quản lý đáng tin cậy nhất của trải nghiệm, xác nhận rằng người dùng có đủ tiền để mua món vật phẩm.

Example purchase flow from client to server through a RemoteEvent
Ví dụ dòng mua hàng từ khách hàng đến máy chủ thông qua một RemoteFunction

Mục tiêu vũ khí

Các tình huống chiến đấu yêu cầu sự chú ý đặc biệt về việc xác minh giá trị, đặc biệt là thông qua việc ngắm bắn và xác minh đánh.

Hãy tưởng tượng một trò chơi mà một người chơi có thể bắn một tia laze vào một người chơi khác.Thay vì khách hàng báo cho máy chủ ai để gây hại, nó nên thay vào đó cho máy chủ biết vị trí nguồn của phát bắn và phần/vị trí nó nghĩ rằng nó đã đánh.Máy chủ có thể xác minh các theo dõi:

  • Vị trí mà khách hàng báo cáo bắn từ gần nhân vật của người chơi trên máy chủ.Lưu ý rằng máy chủ và khách hàng sẽ khác nhau một chút do độ trễ, do đó sẽ cần phải áp dụng thêm sự dung thứ.

  • Vị trí mà khách hàng báo cáo đánh là khá gần vị trí của phần khách hàng báo cáo đánh, trên máy chủ.

  • Không có chướng ngại vật tĩnh giữa vị trí mà khách hàng báo cáo bắn và vị trí mà khách hàng báo cáo bắn.Việc kiểm tra này đảm bảo một khách hàng không cố gắng bắn qua các bức tường.Lưu ý rằng điều này chỉ nên kiểm tra định dạng tĩnh để tránh các phát bắn hợp lệ bị từ chối do độ trễ. Bên cạnh đó , bạn có thể muốn thực hiện thêm xác minh bên máy chủ như sau:

  • Theo dõi khi người chơi cuối cùng bắn vũ khí và xác minh để đảm bảo họ không bắn quá nhanh.

  • Theo dõi lượng đạn của mỗi người chơi trên máy chủ và xác nhận rằng một người bắn đủ đạn để thực hiện cuộc tấn công vũ khí.

  • Nếu bạn đã triển khai đội hoặc một hệ thống chiến đấu "người chơi chống lại bots", xác nhận rằng nhân vật bị trúng là một kẻ thù, không phải là đồng đội.

  • Xác nhận rằng người chơi bị đánh đang sống.

  • Lưu trữ vũ khí và trạng thái người chơi trên máy chủ và xác nhận rằng một người chơi bắn súng không bị chặn bởi một hành động hiện tại như nạp đạn hoặc một trạng thái như chạy.

Manipulation kho dữ liệu

Trong những trải nghiệm sử dụng DataStoreService để lưu dữ liệu người chơi, các kẻ lừa đảo có thể lợi dụng dữ liệu không hợp lệ và các phương pháp khó hiểu hơn, để ngăn chặn một DataStore không lưu đúng cách.Điều này có thể bị lạm dụng đặc biệt trong các trải nghiệm với giao dịch vật phẩm, thị trường và các hệ thống tương tự nơi các vật phẩm hoặc tiền tệ rời khỏi kho hàng của người chơi.

Hãy đảm bảo rằng bất kỳ hành động nào thực hiện thông qua một RemoteEvent hoặc RemoteFunction ảnh hưởng đến dữ liệu người chơi với đầu vào của khách hàng được làm sạch dựa trên những điều theo dõi:

  • Instance giá trị không thể được serialize thành một DataStore và sẽ thất bại. Sử dụng kiểm tra kiểu để ngăn chặn điều này.
  • DataStoresgiới hạn dữ liệu .Các chuỗi có chiều dài ngẫu nhiên nên được kiểm tra và/hoặc giới hạn để tránh điều này, cùng với việc đảm bảo không thể thêm các chìa khóa ngẫu nhiên không giới hạn vào các bảng bởi khách hàng.
  • Các chỉ mục bảng không thể là NaN hoặc nil. Lặp lại trên tất cả các bảng được truyền bởi khách hàng và xác minh tất cả các chỉ mục đều hợp lệ.
  • DataStores chỉ có thể chấp nhận các ký tự UTF-8 hợp lệ, vì vậy bạn nên làm sạch tất cả các chuỗi được cung cấp bởi khách hàng thông qua utf8.len() để đảm bảo chúng hợp lệ. utf8.len() sẽ trả về chiều dài của một chuỗi, xử lý các ký tự tiếng Việt như một ký tự duy nhất; nếu gặp phải một ký tự UTF-8 không hợp lệ, nó sẽ trả về nil và vị trí của ký tự không hợp lệ.Lưu ý rằng các chuỗi UTF-8 không hợp lệ cũng có thể xuất hiện trong các bảng như một chìa khóa và giá trị.

Giảm tốc độ từ xa

Nếu một khách hàng có thể làm cho máy chủ của bạn hoàn thành một hoạt động tốn kém về mặt toán học, hoặc truy cập một dịch vụ giới hạn tốc độ như thông qua một , thì rất quan trọng là bạn phải triển khai giới hạn tốc độ để đảm bảo hoạt động không được gọi quá thường xuyên.Giới hạn tốc độ có thể được thực hiện bằng cách theo dõi khi khách hàng cuối cùng kích hoạt một sự kiện remote và từ chối yêu cầu tiếp theo nếu nó được gọi quá sớm.

Xác nhận di chuyển

Đối với trải nghiệm cạnh tranh, bạn có thể muốn xác minh chuyển động nhân vật người chơi trên máy chủ để đảm bảo họ không dịch chuyển xung quanh bản đồ hoặc di chuyển nhanh hơn mức chấp nhận được.

  1. Trong các bước 1 giây, kiểm tra vị trí mới của nhân vật so với vị trí đã lưu trước đó.

    Image showing moving character's position on a straight path in increments of 1 second
  2. Xác định một thay đổi tối đa "có thể chấp nhận" trong khoảng cách dựa trên nhân vật của WalkSpeed (điểm mỗi giây), nhân với ~1.4 để cho phép một số lơ là đối với độ trễ của máy chủ.Ví dụ, tại một mặc định WalkSpeed của 16, một delta có thể chấp nhận được là ~22.

    Image showing tolerable change in distance based on character's walk speed
  3. So sánh sự thay đổi khoảng cách thực tế so với sự thay đổi delta có thể chấp nhận và tiến hành như sau:

    • Đối với một delta có thể chấp nhận, lưu vị trí mới của nhân vật để chuẩn bị cho lần kiểm tra tiếp theo tăng.
    • Đối với một delta bất ngờ hoặc không thể chịu đựng được (tiềm năng tốc độ/cuộc tấn công dịch chuyển):
      1. Tăng giá trị "number of offenses" riêng biệt cho người chơi, so với việc trừng phạt họ vì "false positive" do độ trễ máy chủ cực đoan hoặc các yếu tố không gian lỗi khác.
      2. Nếu một lượng lớn các vi phạm xảy ra trong khoảng thời gian 30-60 giây, Kick() người chơi từ trải nghiệm hoàn toàn; nếu không, thiết lập lại số lượng "số lần vi phạm".Lưu ý rằng khi đuổi một người chơi vì gian lận, tốt nhất là ghi lại sự kiện để bạn có thể theo dõi được bao nhiêu người chơi bị ảnh hưởng.