Roblox は、分散物理システム を使用して、クライアントがコントロールの物理シミュレーションオブジェクトの物理的シミュレーションに対して、通常はプレイヤーのキャラクターとその近くのアンカーオブジェクトの物理的シミュレーションオブオブジェクトを所有しています。さらに、サードパ
協力的に、これはスキルを持った悪用者があなたのゲームで作弊を可能にする可能性のあるコードを実行することを意味します。これには以下が含まれます:
- 自分のキャラクターを場プレースの周りにテレポートする。
- Class.RemoteEvent|RemoteEvents を発動したり、RemoteFunctions のように、獲得することなくアイテムを獲得するようにします。
- 彼らのキャラクターの WalkSpeed を調整して、本当に速く移動するようにします。
限定されたデザイン防御を実装して、一般的な攻撃をキャッチすることはできますが、サーバー側のミッティングタクティクスをより信頼できるようにすることは、非常にお勧めです。サーバーは実行中のすべてのエクスペリエンスの最終権限を持っています。
防御的デザイン戦略
基本的なデザインの決定は、「最初の一歩」のセキュリティメジャーをサービスすることがあります "最初の一歩" セキュリティメジャーは、他のプレイヤーを倒すことでポイントを獲得するため、悪用者は、ポイントで迅速に殺されるために複数のボットを作成することがあります。この潜在的な悪用に対処
アプローチ | 予測可能な結果 |
---|---|
コードを書き込んで、ボットを追跡してください。 | |
新たにスポーンしたプレイヤーのキルに対するポイント獲得を減少するか、完全に削除する。 |
防御的なデザインは明らかに完璧で包括的なソリューションではないため、サーバー側のミットレーション と一緒により広いセキュリティアプローチに貢献できます。
サーバー側ミッティング
できるだけ多く、サーバー は最終的な結論を내り、世界の現在の状態と「真実」の状操作の間をクライアントがリクエストすることができます。クライアントは、サーバー に変更をリクエストするか、アクションを実行することができますが、サーバーは結果を他のプレイヤー
特定の物理オペレーションを除き、クライアントのデータモデルの変更はサーバーにレプリケートされませんので、メインの攻撃パスは通常、 RemoteEvents と RemoteFunctions を介してネットワークイベントを宣言したネットワークイベントです。クライ
リモートランタイムタイプの検証
1つの攻撃パスは、エクスプロイターが RemoteEvents と RemoteFunctions の引数を持つ不正なタイプのコードを呼び出すために使用されます。一部のシナリオでは、サーバーがこれらのリモートにリスニングすると、エクスプロイターがコードを不正なタイプのコードに変換して、サ
リモートイベント/機能を使用すると、サーバー上のパスされた引数の タイプ を有効にすることで、このタイプの攻撃を防ぐことができます。モジュール 「t」 、サーバー上で利用可能なモジュー
StarterPlayerScripts のローカルスクリプト
local ReplicatedStorage = game:GetService("ReplicatedStorage")local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")-- 関数を呼び出すときにパーツの色と位置をパスlocal newPart = remoteFunction:InvokeServer(Color3.fromRGB(200, 0, 50), Vector3.new(0, 25, 0))if newPart thenprint("The server created the requested part:", newPart)elseif newPart == false thenprint("The server denied the request. No part was created.")end
ServerScriptService のスクリプト
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = ReplicatedStorage:WaitForChild("RemoteFunctionTest")
local t = require(ReplicatedStorage:WaitForChild("t"))
-- 必要ないオーバーヘッドを避けるために、タイプの有効化を事前に作成します
local createPartTypeValidator = t.tuple(t.instanceIsA("Player"), t.Color3, t.Vector3)
-- パスプロパティを使用して新しいパーツを作成
local function createPart(player, partColor, partPosition)
-- パスした引数をチェックします
if not createPartTypeValidator(player, partColor, partPosition) then
-- ここでタイプチェックが失敗した場合、「false」を静かに返す
-- クールダウンなしでエラーを引き起こすと、サーバーをボグダウンできます
-- 代わりにクライアントのフィードバックを提供してください!
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
-- 「createPart()」をリモート関数のコールバックにバインド
remoteFunction.OnServerInvoke = createPart
データ検証
エクスプロイターが発動する可能性のある別の攻撃は、技術的に有効なタイプを送信しますが、長さ、または他の形式で非常に大きく、長く、または楕円形で不正な形式に変換します。たとえば、サーバーが長さに対応するストリングのコストがかかるオペレーションを実行する必要がある場合、エクスプロイターは不正な
同様に、 both inf と both NaN は、type() として、1> number1> を引き起こすが、両方とも、フォロー中の関数を通じて不正に処理される可能性があります:
local function isNaN(n: number): boolean
-- NaN は自分自身とは等しくありません
return n ~= n
end
local function isInf(n: number): boolean
-- 数字は -inf または inf です
return math.abs(n) == math.huge
end
エクスプロイターが使用する他の一般的な攻撃は、tables の代わりに Instance を送信することです。複雑な貨物は、通常のオブジェクトの参照を偽装することがあります。
たとえば、体験中ショップシステムを提供し、価格のようなアイテムデータを NumberValueするなど、エクスプロイターは、フォロー中のコードを実行することですべての他のチェックを回避できます:
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, -- ネガティブ値も使用でき、結果として通貨を与える代わりに取得できます!},}-- サーバーに悪意のある貨物を送信 (これは拒否されます)print(buyItemEvent:InvokeServer(payload)) -- 「 false 無効なアイテムを提供しました」-- サーバーに実際のアイテムを送信します (これは通過します!)print(buyItemEvent:InvokeServer(itemDatafolder["Real Blade"])) -- Outputs "true" and remaining currency if purchase succeeds
ServerScriptService のスクリプト
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local itemDataFolder = ReplicatedStorage:WaitForChild("ItemData")
local buyItemEvent = ReplicatedStorage:WaitForChild("BuyItemEvent")
local function buyItem(player, item)
-- パスされたアイテムがスパフされていないことを確認し、ItemData フォルダにあること
if typeof(item) ~= "Instance" or not item:IsDescendantOf(itemDataFolder) then
return false, "Invalid item provided"
end
-- サーバーはその後、以下の例のフローに基づいて購入を処理できます
end
-- 「buyItem()」をリモート関数のコールバックにバインド
buyItemEvent.OnServerInvoke = buyItem
値の検証
有効なタイプ と データ を確認するだけでなく、値 を通過する Class.RemoteEvent|RemoteEvents と 1> Class.RemoteFunction|RemoteFunctions1> を確認する必要があります。4> コンテキストが
インエクスペリエンスショップ
ユーザーインターフェイスを持つインエクスペリエンスショップシステム、たとえば「購入」ボタンのある製品選択メニューを考えてください。ボタンを押すと、RemoteFunction を介してクライアントとサーバーの間で Class.RemoteFunction を呼び出すことが
武器ターゲティング
戦闘シナリオでは、特に照準とヒット検証を通じて、値を有効にすることに注意が必要です。
プレイヤーがレーザービームを別のプレイヤーに発射できるゲームを想像してください。クライアントがサーバーに 誰 にダメージを与えるかを告知する代わりに、サーバーはショットのオリジンポジションと部品/ポジションを告げる必要があります。サーバーはフォロー中に検証できます:
クライアントが報告する位置 Shooting from は、サーバー上のプレイヤーのキャラクターの近くにあります。注意してください、サーバーとクライアントはラテンシーのためにわずかに異なるので、追加の忍耐性が必要になります。
クライアントが報告する位置は、ヒットするのは、クライアントが報告するパーツの位置と比較して、理由があります。
クライアントがレポートする位置とクライアントがレポートする位置の間には、静的な障害がありません。このチェックは、クライアントがレポートする位置とクライアントがレポートする位置の間に障害がないことを確認します。このチェックは、遅延により有効なショットが拒否されることを避けるためにのみ静的なジオメトリのみをチェックすることを確認します。 追加して 、以下のサーバー側の検証を実装してください:
プレイヤーが武器を最後に発射したときにトラックし、過度に速く撃っていないことを確認します。
サーバー上で各プレイヤーの弾薬量を追跡し、発射プレイヤーが武器攻撃を実行するのに十分な弾薬を持っていることを確認します。
チームを実装したか、または「プレイヤー対ボット」の戦闘システムを実装した場合、ヒットキャラクターが敵であることを確認します。
ヒットプレイヤーが生存していることを確認します。
サーバーに武器とプレイヤー状態を保存し、実行中のプレイヤーがスプリントなどの現在のアクションによってブロックされていないことを確認します。
データストア操作
Class.DataStoreService を使用してプレイヤーのデータを保存するエクスペリエンスで、悪用者は無効な データ 、およびより難読なメソッドを悪用して、DataStore が適切に保存されないようにすることがあります。これは特にアイテム取引
プレイヤーのデータにクライアントの入力に基づいて影響を与える RemoteEvent または RemoteFunction を実行していることを確認してくださフォロー中:
- DataStores には データ制限 があります。任意の長さのストリングはチェックし、/または 制限なしの任意のキー を追加してこれを避け、クライアントが限定なしの任意のキーをテーブルに追加できないようにします。
- テーブルインデックスは NaN または nil ではありません。クライアントによってパスされたすべてのテーブルをイテレートし、すべてのインデックスを有効にする必要があります。
- DataStores は、utf8.len() を通じて、クライアントによって提供されるすべての UTF-8 文字を受信できるの
リモートスロットリング
クライアントが、あなたのサーバーを計算コストの高いオペレーション、または DataStoreService などのレート制限付きサービスにアクセスすることができる場合、RemoteEvent を実装することは重要です。レート制限は、クライアント
移動検証
コンペティティブなエクスペリエンスの場合、サーバー上のプレイヤーキャラクターの動きを検証して、マップ上をテレポートしたり、容認された以上に動くことを確認して、問題なく動作することを確認してください。
1秒のインクリメントで、キャラクターの新しい場所を以前にキャッシュされた場所と比較します。
キャラクターの Class.Humanoid.WalkSpeed|WalkSpeed (スタッド/秒)を基準にして、最大の "忍耐可能" な変更を距離で決定します。たとえば、デフォルトの Class.Humanoid.WalkSpeed の 16 で、トレーラーのラテンシーに対するある程度の寛容度を許可するた
実際の距離デルタと耐受可能なデルタの間の比較を行い、次の手順に従って進行します:
- 耐えられるデルタの場合、キャラクターの新しい場所を準備して、次の増加したチェックに備えてください。
- 予期せぬまたは耐えられないデルタ (潜在的な速度/テレポート悪用):
- プレイヤーに対する「犯罪の数」の値を増加させ、極端なサーバーラテンシーや他の非エクスプロイト要因による「偽のポジティブ」の結果でペナルティを科す。
- 30-60秒の期間で多くのオフェンスが発生する場合、 Kick() エクスペリエンス全体からプレイヤーを完全に削除します。そうでない場合は、「オフェンスの数」カウントをリセットします。チートでプレイヤーを追放する場合は、 イベントログを記録することが