• Из-за обновления GTA 5 (был добавлен новый патч) может временно не работать вход в RAGE Multiplayer.

    ERROR: Your game version is not supported by RAGE Multiplayer.

    Данная ошибка говорит о том, что GTA V обновилась до новой версии (GTA Online тоже). Вам необходимо обновить саму игру в главном меню вашего приложения (Steam / Epic Games / Rockstar Games).
    Если после этого RAGE:MP все равно не работает - вам нужно дождаться выхода патча для самого мультиплеера (обычно это занимает от нескольких часов до нескольких дней).

    Новости и апдейты Rockstar Games - https://www.rockstargames.com/ru/newswire/
    Статус всех служб для Rockstar Games Launcher и поддерживаемых игр: https://support.rockstargames.com/ru/servicestatus


    Grand Theft Auto 5 (+ GTA Online) последний раз были обновлены:

FAQ Как не стоит делать: безопасность ивентов и клиентских скриптов

welaurs

Начинающий специалист
Автор темы
30 Ноя 2021
19
46
48
Дисклеймер. Сказанное ниже является личным мнением автора. Сам по себе автор является мастером спорта международного класса по продавливанию дивана и ничего полезного в своей жизни он, разумеется, не сделал. Урок не претендует на звание истины в последней инстанции, а автор готов признавать свои ошибки и публично приносить извинения, если заденет чьи-то чувства прекрасного своей графоманией с шизофреническими наклонностями.

Изучив некоторые моды, создаётся ощущение, что авторы кода не понимают реальной опасности кода, который доверяет клиенту. Рассмотрю пример, взятый из мода самая последняя сборка RedAge (от Golem) от 19.08.21 и объясню возможные последствия подобной уязвимости.

Отсутствие валидации ивентов, которые присылает клиент. Я более чем уверен, что данная проблема существует не только в данном моде. Вполне вероятно, что подобное есть и в модах "топовых" проектов. С целью защиты последних, я не буду подробно расписывать алгоритм эксплуатации этих уязвимостей, а лишь поверхностно расскажу принцип работы. Хотя кому я лгу: создатели читов уже проверили нижеописанное и ничего не нашли. Или нет...

Опасность: высокая. Разработчики RAGEMP добавили мнимую безопасность ресурсов, которые загружаются на клиент: сделали шифрование контента (dlcpacks и скриптов в том числе), захэшировали имена файлов и, вероятнее всего, применили другие извращения, но все эти потуги способны остановить только ленивых или слабеньких в реверсе. При должном умении и желании ваши ресурсы будут преобразованы в тот вид, в котором они хранятся на сервере. Никакие JS-обфускаторы [будь то бесплатный javascript-obfuscator или супер-дорогой jsscrambler], ваши исхищрения с eval (когда-то на Radmir RP видел такое) на клиенте не уберегут от возможности анализировать код. А это значит, что абсолютно любой человек может получить имена ивентов, посмотреть примеры аргументов и подделывать вызовы со стороны клиента. Держите это в голове при написании какой-либо системы, которая требует взаимодействия клиент -> сервер.

В моде RedAge беглым взглядом по серверному коду я нашёл одну подобную уязвимость. Она не способна сломать серверную экономику, но подпортить настроение игрокам, которым незаслуженно выдали розыск - легко.
Исходный код с уязвимостью:
C#:
[RemoteEvent("clearWantedLvl")]
public static void clearWantedLvl(Player sender, params object[] arguments)
{
    try
    {
        var target = (string)arguments[0];
        Player player = null;
        try
        {
            var pasport = Convert.ToInt32(target);
            if (!Main.PlayerNames.ContainsKey(pasport))
            {
                Notify.Send(sender, NotifyType.Error, NotifyPosition.BottomRight, $"Паспорта с таким номером не существует", 3000);
                return;
            }
            player = NAPI.Player.GetPlayerFromName(Main.PlayerNames[pasport]);
            target = Main.PlayerNames[pasport];
        }
        catch
        {
            target.Replace(' ', '_');
            if (!Main.PlayerNames.ContainsValue(target))
            {
                Notify.Send(sender, NotifyType.Error, NotifyPosition.BottomRight, $"Игрок не найден", 3000);
                return;
            }
            player = NAPI.Player.GetPlayerFromName(target);
        }

        var split = target.Split('_');
        MySQL.Query($"UPDATE characters SET wanted=null WHERE firstname='{split[0]}' AND lastname='{split[1]}'");
        try
        {
            setPlayerWantedLevel(player, null);
        }
        catch { }
        Notify.Send(sender, NotifyType.Success, NotifyPosition.BottomRight, $"Вы сняли розыск с владельца паспорта {target}", 3000);
    } catch (Exception e) { Log.Write("ClearWantedLvl: " + e.Message, nLog.Type.Error); }
}
Вариант исправления уязвимости: сервер полностью валидирует требования клиента. В данном случае необходимо проверить, действительно ли sender имеет право устанавливать уровень розыска любому игроку.

Другие статьи:
Как не стоит делать: база данных и работа с ней на примере RedAge

[JS] Как подключить отладчик (Debugger) в PhpStorm для server-side
 
Последнее редактирование:

Harland David Sanders

Куратор портала
Команда форума
Куратор портала
VIP
high coder
media
10 Сен 2020
3,060
2,451
219
Я тоже несколько раз (в других темах) предупреждал людей быть осторожными с теми данными которые игрок отправляет серверу в эвентах и обязятельно повторно проверять их корректность.. а ты молодец описал все в одной теме просто и понятно (y)
 
  • Like
Реакции: welaurs

Aiden

Гуру
high coder
7 Сен 2021
249
158
102
Разработчики RAGEMP добавили мнимую безопасность ресурсов, которые загружаются на клиент: сделали шифрование контента (dlcpacks и скриптов в том числе), захэшировали имена файлов и, вероятнее всего, применили другие извращения, но все эти потуги способны остановить только ленивых или слабеньких в реверсе.
Всё это "шифрование" рейджа обходится за минуту. Всех тонкостей я раскрывать не буду, но скажу бегло, что при загрузке клиентских файлов их можно выгрузить в исходном состоянии без всяких там "Superior encrypt by RAGEMP". Так же, в теории, трассировкой кода клиента рейдж(кого-то останавливал VMProtect?) можно поискать функции шифрования и, как бы это логично не звучало, сделать декриптор. Почему в теории? -Потому что я сам этого не делал

Вывод: разработчикам серверов изначально нужно шифровать свой клиент и не надеятся на рейдж. Как минимум, это затянет время на расшифровку и человек, который это делает, скажет "Да ну его...."
 

welaurs

Начинающий специалист
Автор темы
30 Ноя 2021
19
46
48
Всё это "шифрование" рейджа обходится за минуту. Всех тонкостей я раскрывать не буду, но скажу бегло, что при загрузке клиентских файлов их можно выгрузить в исходном состоянии без всяких там "Superior encrypt by RAGEMP". Так же, в теории, трассировкой кода клиента рейдж(кого-то останавливал VMProtect?) можно поискать функции шифрования и, как бы это логично не звучало, сделать декриптор. Почему в теории? -Потому что я сам этого не делал

Вывод: разработчикам серверов изначально нужно шифровать свой клиент и не надеятся на рейдж. Как минимум, это затянет время на расшифровку и человек, который это делает, скажет "Да ну его...."
Если мы начали говорить о защите, то стоит упомянуть, что функцию шифрования можно заинлайнить под VMP. Но в таком случае при использовании, например, AES даже с коротким ключом в 128 бит нагрузка на клиент будет, мягко говоря, высокая. Если они написали какой-то свой алгоритм, то тоже не проблема: всегда остаётся GTA, которая точно не будет заниматься расшифровкой RAGEMP моделей, а также v8, который требует скрипт в виде текста и расшифровкой, как ни странно, он тоже не будет заниматься.
 
Последнее редактирование:

ilhmjv

Начинающий специалист
18 Июл 2021
75
2
45
Если мы начали говорить о защите, то стоит упомянуть, что функцию шифрования можно заинлайнить под VMP. Но в таком случае при использовании, например, AES даже с коротким ключом в 128 бит нагрузка на клиент будет, мягко говоря, высокая. Если они написали какой-то свой алгоритм, то тоже не проблема: всегда остаётся GTA, которая точно не будет заниматься расшифровкой RAGEMP моделей, а также v8, который требует скрипт в виде текста и расшифровкой, как ни странно, он тоже не будет заниматься.
В вебе обычно для этого юзают coockie токены, но на сколько я понимаю в CEF нету доступа к coockie, ну или хотя бы к localStorage, хотя и там хранить захешированные пароли не очень. Тогда каким образом проверять доступ игрока? Через ник? Искать в бд ник и проверять какие он имеет права, но я думаю что это не совсем безопасно так как любой уже внутри игры может вызвать ивент и передать туда любой ник в качестве аргумента. И так как никакие данные на стороне клиента о игроке кроме как сам обьект не хранятся, то вопрос че тогда делать с этим? Токен доступа отправлять каждый раз новый, и хранить его прям в состоянии реакта например?
 

ready to massacre

Активный участник
4 Мар 2023
153
92
40
В вебе обычно для этого юзают coockie токены, но на сколько я понимаю в CEF нету доступа к coockie, ну или хотя бы к localStorage, хотя и там хранить захешированные пароли не очень. Тогда каким образом проверять доступ игрока? Через ник? Искать в бд ник и проверять какие он имеет права, но я думаю что это не совсем безопасно так как любой уже внутри игры может вызвать ивент и передать туда любой ник в качестве аргумента. И так как никакие данные на стороне клиента о игроке кроме как сам обьект не хранятся, то вопрос че тогда делать с этим? Токен доступа отправлять каждый раз новый, и хранить его прям в состоянии реакта например?
на сервере хранить сессию авторизации пользователя, банально player.userId = 1, все у тебя пользователь с userId = 1, далее где нужно select roles from users where userId = 1, if roles.has(admin) then do admin things
 

ilhmjv

Начинающий специалист
18 Июл 2021
75
2
45
на сервере хранить сессию авторизации пользователя, банально player.userId = 1, все у тебя пользователь с userId = 1, далее где нужно select roles from users where userId = 1, if roles.has(admin) then do admin things
В обьекте mp.players.local на стороне сервера добавить поле о его роли с БД при авторизации? То есть например: mp.players.local.userRoleKind = 1? Можно ли в качестве БД юзать MongoDB для RP сервера? опыта с MySQL или подобными реляционными не имеел.
 

ready to massacre

Активный участник
4 Мар 2023
153
92
40
В обьекте mp.players.local на стороне сервера добавить поле о его роли с БД при авторизации? То есть например: mp.players.local.userRoleKind = 1? Можно ли в качестве БД юзать MongoDB для RP сервера? опыта с MySQL или подобными реляционными не имеел.
На сервере хоть где, (mp.players.local на сервере нет), MongoDB можно
и монга хороша, и mysql/pg/maria тд хороши, главное уметь с ними работать, правильно строить индексы и тд
в плане разработки монга более гибкая за счет не строгой структуры документов в коллекциях (не придется под каждый чих писать миграцию)

а по поводу защиты данных, еще хорошая превентивная мера будет это логировать (с помощью чего угодно, например редис) все важные взаимодействия на сервере и бэкапить раз в N времени (условно раз в день), чтобы в случае чего с бэкапа восстановиться
 

ilhmjv

Начинающий специалист
18 Июл 2021
75
2
45
На сервере хоть где, (mp.players.local на сервере нет), MongoDB можно
и монга хороша, и mysql/pg/maria тд хороши, главное уметь с ними работать, правильно строить индексы и тд
То есть сервер немного по другому работает в RAGE если сравнивать с обычным бекендом? Я могу просто сохранить идшник пользователя например в простой переменной?(let playerId; playerId = id
 

ready to massacre

Активный участник
4 Мар 2023
153
92
40
То есть сервер немного по другому работает в RAGE если сравнивать с обычным бекендом? Я могу просто сохранить идшник пользователя например в простой переменной?(let playerId; playerId = id
да, если что, и на обычном бэкенде так можно (например в той же ноде), только если это не пых (запустился, отработал, сдох) или аналогичные ему интерпретируемые языки
 
  • Like
Реакции: Inoi

ilhmjv

Начинающий специалист
18 Июл 2021
75
2
45
да, если что, и на обычном бэкенде так можно (например в той же ноде), только если это не пых (запустился, отработал, сдох) или аналогичные ему интерпретируемые языки
Понял, то есть получается сервер хранит данные для каждого конкретного пользователя и переменные "живут" пока пользователь авторизован.
 

ready to massacre

Активный участник
4 Мар 2023
153
92
40
Понял, то есть получается сервер хранит данные для каждого конкретного пользователя и переменные "живут" пока пользователь авторизован.
в зависимости куда ты эту переменную сохраняешь, а так по дефолту обычно пока включен сервер (1), пока жив объект игрока (2, к примеру) и тд
 

ilhmjv

Начинающий специалист
18 Июл 2021
75
2
45
в зависимости куда ты эту переменную сохраняешь, а так по дефолту обычно пока включен сервер (1), пока жив объект игрока (2, к примеру) и тд
ну я имею ввиду глобальную переменную, если она локальная внутри зоны видимости функции то конечно же он откинется как только функция перестанет работать