Cum securizezi evenimentele de server RedM: fiecare TriggerServerEvent este input de atacator
Securizarea evenimentelor de server RedM: de ce orice TriggerServerEvent este input de atacator, regulile de validare de aplicat și ce adaugă un anticheat.
Dacă vrei securizarea evenimentelor de server RedM, pornește de la un singur fapt: jucătorul controlează clientul, deci controlează fiecare argument al fiecărui TriggerServerEvent. Banii și obiectele care dispar de pe serverele de roleplay pleacă rareori printr-un hack ingenios de memorie. Pleacă printr-un handler care a avut încredere într-un număr primit.
Ghidul este pentru dezvoltatorii de resurse și pentru proprietarii de servere care revizuiesc scripturi. Exemplele sunt în Lua, neutre față de framework. Aceleași reguli se aplică oricărei resurse VORP, RSG sau proprii și sunt ele care fac un exploit obișnuit de trigger pe RedM să eșueze în loc să plătească.
De ce un eveniment declanșat de client este input controlat de atacator
Un eveniment de server este o ușă publică. Scriptul tău de client apelează TriggerServerEvent("shop:sell", item, count, price), iar serverul rulează handler-ul. Nimic din acel apel nu dovedește că cererea vine din scriptul tău. Un mod menu sau o resursă injectată poate apela același eveniment, cu același nume, și poate pune ce vrea în argumente.
Ascunderea numelui evenimentului nu ajută. Lua-ul de client ajunge la fiecare jucător, iar numele evenimentelor apar în el. Tratează numele drept public și argumentele drept ostile, cum tratează un dezvoltator web un câmp de formular.
Aceasta este întreaga clasă de exploit-uri VORP într-o propoziție: un handler care aplică ce spune clientul. Restul ghidului arată cum încetezi să faci asta.
Un handler vulnerabil și cel reparat
Iată forma pe care o vedem cel mai des. Clientul trimite obiectul, cantitatea și prețul, iar serverul plătește.
RegisterNetEvent('shop:sell', function(item, count, price)
local src = source
-- ia de bun tot ce spune clientul
removeItem(src, item, count)
addMoney(src, count * price)
end)Fiecare linie este un bug. Cel care apelează alege prețul, deci un singur apel cu un price uriaș bate monedă. Un count negativ transformă scoaterea într-un cadou. Este posibil ca un obiect pe care jucătorul nu îl deține să nu fie verificat niciodată. Handler-ul nici nu întreabă dacă jucătorul se află la un magazin.
Versiunea reparată ține prețurile pe server, validează tipurile și intervalele, verifică jucătorul față de lume și refuză discret când ceva nu se potrivește. removeItem, addMoney și getItemCount reprezintă funcțiile propriului tău framework.
local PRICES = { apple = 2, bread = 3 } -- autoritatea stă aici
local SHOP = vector3(-324.0, 803.0, 117.0) -- coordonate de exemplu
local lastCall = {}
local function refuse(src, why)
print(('[shop:sell] refused src=%s reason=%s'):format(src, why))
end
RegisterNetEvent('shop:sell', function(item, count)
local src = source -- nu te încrede niciodată într-un id venit de la client
local now = GetGameTimer()
if lastCall[src] and now - lastCall[src] < 1000 then
return refuse(src, 'rate')
end
lastCall[src] = now
if type(item) ~= 'string' or type(count) ~= 'number'
or count % 1 ~= 0 or count < 1 or count > 50 then
return refuse(src, 'bad_args')
end
local price = PRICES[item]
if not price then return refuse(src, 'unknown_item') end
local ped = GetPlayerPed(src)
if #(GetEntityCoords(ped) - SHOP) > 5.0 then
return refuse(src, 'too_far')
end
if getItemCount(src, item) < count then
return refuse(src, 'not_owned')
end
removeItem(src, item, count)
addMoney(src, count * price) -- serverul calculează recompensa
end)
AddEventHandler('playerDropped', function() lastCall[source] = nil end)Clientul trimite acum două valori, iar serverul le tratează pe amândouă drept sugestii. Fiecare regulă din acel handler merită un paragraf al ei.
Regulile care securizează evenimentele de server RedM
1. Identifică jucătorul prin source, și numai prin source
În interiorul unui handler de eveniment de server, source este jucătorul care l-a declanșat și este setat de platformă, nu de client. Captează-l într-o variabilă locală chiar la început (local src = source), pentru că se poate schimba după un yield. Nu accepta niciodată un argument „player id” sau „target id” ca să decizi cine este plătit sau cine este rănit.
2. Nu accepta ca adevăr sume, prețuri, nume de obiecte sau ținte
Dacă clientul trebuie să spună ce vrea, caută valoarea pe server. Prețurile, recompensele și listele de obiecte aparțin unui tabel de pe server. Dacă un argument este o țintă (alt jucător, o entitate, o coordonată), verifică dacă există și dacă cel care apelează are voie să acționeze asupra ei. Cele mai sigure evenimente nu au aproape niciun argument.
3. Validează tipurile și intervalele
Lua va trece fără probleme un tabel acolo unde așteptai un string, sau -1, sau un float, sau un număr atât de mare încât aritmetica o ia razna. Verifică type(), cere numere întregi acolo unde vrei numere întregi și fixează o limită superioară fermă, potrivită cu ce poate face legitim funcționalitatea. Respinge, nu limita: o cerere limitată nu îți spune în continuare nimic despre cine a trimis-o.
4. Verifică distanța, jobul și starea pe server
O vânzare legitimă se petrece la un magazin, o fabricare legitimă la un banc de lucru, o arestare legitimă prin lege. Verifică asta din datele serverului: poziția jucătorului (cu OneSync funcționează native de server precum GetPlayerPed și GetEntityCoords), jobul lui din framework, starea curentă din propriile tale tabele. Faptul că interfața clientului ascunde un buton nu este o verificare.
5. Lasă serverul să calculeze recompensa
Cel mai puternic tipar este și cel mai simplu: clientul spune „am terminat”, serverul decide cât valora asta. Pentru activități mai lungi, pornește activitatea pe server și înregistrează când. Când clientul raportează finalizarea, verifică dacă a trecut destul timp și dacă există o sesiune, apoi plătește o sumă stabilită pe server.
6. Limitează rata fiecărui eveniment care mută valoare
Un handler corect o dată poate fi totuși apelat de două sute de ori pe secundă. Ține un timestamp sau un contor per jucător și renunță la ce depășește ce ar putea face un om. Curăță tabelul la playerDropped, ca în exemplu. Reluările și inundațiile de cereri solicită și serverul, așa că un return timpuriu ieftin contează.
7. Nu expune evenimente de admin către clienți
Un eveniment numit admin:giveMoney care nu verifică nimic este un cadou. Dacă uneltele de staff trebuie să folosească evenimente, verifică de fiecare dată cel care apelează pe server, cu sistemul de permisiuni al framework-ului sau cu ACE, nu cu un flag din client. Și mai bine, folosește comenzi înregistrate pe server sau evenimente declanșate exclusiv de la server la server.
8. Înregistrează refuzurile
Funcția refuse de mai sus nu este decor. Un refuz este un semnal: fie un bug în propriul tău script de client, fie cineva care testează. O linie per refuz, cu jucătorul și motivul, transformă jurnalele tale într-o listă de piste. O precauție: un refuz singur nu dovedește nimic, deci verifică înainte să banezi.
Unde merge prost în practică
Scripturi moștenite. O resursă plătită sau scursă care are încredere în argumentele ei rămâne vulnerabilă până o citește cineva. Caută în resursele serverului
RegisterNetEventșiRegisterServerEventși citește fiecare handler care atinge bani, obiecte, joburi sau inventar.Evenimente de debug „temporare”. Evenimentul adăugat pentru teste și niciodată scos.
Încrederea într-un flag setat de client.
isAdmin,isOnDutyși valori similare trimise ca argumente sunt tot input de atacator.Repararea unui singur handler. Dacă trei resurse plătesc din aceeași funcție de inventar, apelantul cel mai slab decide securitatea ta.
Când paguba e deja făcută, tiparul din date arată ca un dupe sau ca o valoare atipică bruscă de avere. Articolul nostru despre exploit-ul de dupe de bani pe RedM arată cum îl recunoști dinspre economie.
Ce adaugă un anticheat peste protecția evenimentelor RedM
Handler-ele atente elimină vulnerabilitatea. Un anticheat urmărește oamenii care încearcă oricum și handler-ele pe care nu le-ai revizuit încă. Baobab AC face asta pe partea de server, iar în catalogul lui patru module contează aici. Vezi toate funcționalitățile pentru lista completă.
Anti Blacklist Trigger împiedică meniurile de cheat să apeleze evenimente de server ascunse pentru lucruri precum schimbări neautorizate de job sau ocolirea regulilor de economie.
Anti Spam Trigger blochează inundarea cu cereri menită să copleșească backend-ul serverului.
Anti Economy Trigger Exploit împiedică trișorii să injecteze comenzi ascunse false ca să-și dea bani, aur sau obiecte de valoare.
Anti Native Injection agață native-urile care creează entități și schimbă starea jucătorului, astfel încât un spawn sau o schimbare de stare pe care nicio resursă de server nu le-a cerut este prins pe loc.
În planurile Pro și Network, Economy Forensics adaugă un audit în regim de doar citire al bazei de date a jocului, confruntat cu timpul jucat, pe care îl știe doar anticheat-ul: masa monetară în timp, valori atipice de avere, dupe-uri de obiecte, stackuri imposibile. Fiecare rezultat este o pistă pe care o verifică un om, niciodată o sancțiune automată. Detecția este agnostică față de framework, iar VORP Core este referința pentru modulele de economie. Ghidul anticheat VORP intră în detaliile acelei configurări, iar cum este prins un mod menu explică partea de detecție a aceleiași povești.
Poți vedea cum arată rezultatul în panoul demo și poți compara planurile pe pagina de prețuri.
Ce nu poate face un anticheat
Încă trei limite merită spuse. Detecția este probabilistă, deci postura livrată este observarea: o detecție îndoielnică costă o linie într-o listă, nu un kick. Economy Forensics îți spune că ceva pare greșit, nu de ce, iar un om trebuie să citească. Iar un anticheat vede clientul și traficul, nu logica ta de business, deci doar tu știi că o vânzare de 400 de dolari la acel magazin este imposibilă.
Cele două straturi se completează. Autoritatea pe server face exploit-ul imposibil. Anticheat-ul prinde încercarea, iar auditul arată ce a trecut înainte să repari. Dacă vrei argumentul mai larg pentru care încrederea trebuie să stea pe server, citește anticheat server-side vs client-side.
O listă de verificare pentru a securiza astăzi evenimentele de server RedM
- 1
Listează fiecare
RegisterNetEventșiRegisterServerEventdin resursele tale. - 2
Pentru fiecare care atinge bani, obiecte, joburi sau inventar, citește handler-ul.
- 3
Înlocuiește fiecare preț, recompensă și id de jucător venit de la client cu o căutare pe server sau cu
source. - 4
Adaugă verificări de tip și de interval, apoi o verificare de poziție sau de stare.
- 5
Adaugă o limită de rată per jucător și un jurnal al refuzurilor.
- 6
Elimină evenimentele de debug și de admin care sar peste verificări.
- 7
Abia apoi pornește modulele de anticheat, ca să acoperi ce a scăpat revizuirii. Începe în observare, citește alertele și strânge treptat. Cum funcționează un anticheat RedM explică fluxul.
Un handler încrezător este ieftin de scris și scump de găsit. O oră petrecută citind acum evenimentele serverului costă mai puțin decât o noapte cu economia furată.