Рубрики
Без рубрики

Вы всегда должны выполнять проверку на стороне сервера! Всегда!

Оригинальная статья с другим примером на моем личном веб-сайте (https://petrepopescu.tech/2021/01/you-should-always-do-server-side-validation-always/) Веб – технологии сильно эволюционировали в прошлом…

Автор оригинала: Petre Popescu.

Оригинальная статья с другим примером на моем личный веб-сайт

Веб-технологии сильно эволюционировали за последние несколько лет, как на стороне сервера, так и на стороне клиента. Существует множество веб-фреймворков, наборов пользовательского интерфейса, библиотек JavaScript и всего, что вам нужно для простой и быстрой разработки веб-сайта или веб-приложения. Библиотеки JavaScript повзрослели и стали очень мощными, а вычислительная мощность, даже на компьютерах низкого класса, в настоящее время не представляет проблемы для большинства веб-приложений. Из-за этого веб-приложения стали более сложными, пользовательские интерфейсы более причудливыми и… программисты ленивыми.

Поскольку фреймворки JavaScript настолько мощны, у многих возникает соблазн написать бизнес-логику в клиентской части приложения. Хотя это может сэкономить вычислительную мощность сервера за счет разгрузки вычислительных задач пользователю и настоятельно рекомендуется, когда это возможно, это может создать некоторые проблемы с безопасностью. Это особенно верно, если пользователь может отправлять данные на сервер с помощью форм или другого метода ввода.

Из-за этого всегда выполняйте проверку любого запроса на стороне сервера, даже если у вас (обычно) есть клиент между сервером и пользователем. Давайте рассмотрим три сценария и то, как неспособность проверить полученные входные данные создает уязвимость в системе безопасности. Кроме того, хотя может показаться очевидным, что проблема существует, это не всегда так, и вариации из всех трех сценариев были реальными проблемами, которые я видел в реальных продуктах.

Сценарий 1: Повышение привилегий путем изменения роли пользователя

В первом сценарии мы рассмотрим приложение, которое имеет разные роли для зарегистрированного пользователя. Это может быть любое такое веб-приложение, но я буду рассматривать его как систему управления контентом, такую как WordPress. Здесь у пользователей могут быть разные роли: Администратор, Редактор, Участник, Обычный пользователь. Любой пользователь, являющийся участником или выше, может изменить роль другого пользователя, но он не может предоставлять более высокие привилегии, чем те, которые у него есть. Например, Редактор не может назначить кого-то другого Администратором.

Для этого на странице редактирования пользователя есть форма с информацией о пользователе и раскрывающийся список с возможными ролями пользователей. Страница отображается с использованием механизма шаблонов (в этом примере я буду использовать Twirl из Play Framework), и приемлемые значения действительно предоставляются сервером. Наш злоумышленник является “Участником”, поэтому для него должны быть доступны только две роли.


Теперь наш программист подумал, что он умен, и для значения он явно установил его в хэш идентификатора. Это хэш идентификатора роли в базе данных, так как приложение довольно сложное, и при необходимости могут быть добавлены новые роли. Он даже следит за тем, чтобы полученное при сохранении пользователя было действительным и соответствовало реальной роли. Метод, обрабатывающий запрос, выглядит примерно так.

public Result saveUserRole(Http.Request request) {
    Form form = formFactory.form(UserInfoDTO.class).bindFromRequest(request);
    if (form.hasErrors()) return badRequest();
    
    List roles = rolesDao.getRoles();
    Role submitedRole = null;
    for (Role role:roles) {
        if (role.getIdHash() == form.get().role()) {
            submitedRole = role;
            break;
        }
    }

    if (role == null) return badRequest();

    UserDO user = userDao.find(form.get().userId());
    if (user == null) return notFound();

    user.setRole(role);
    userDao.update(user);

    return ok();
}

На первый взгляд, все вроде бы в порядке. Форма проверена, роль проверена, пользователь запрашивается из базы данных, и давайте даже предположим, что существует токен CSFR, который защищает от вредоносных отправок. Можете ли вы определить проблему? Сервер предполагает, что, поскольку код пользовательского интерфейса был сгенерирован на сервере, полученная роль является одной из ролей, которые пользователь имеет разрешение предлагать. Нет никакой проверки, чтобы убедиться, что приписываемая роль не выше, чем у текущего пользователя.

Наш злоумышленник может просто открыть инспектор в браузере и отредактировать HTML, чтобы добавить новое значение в <выбрать>

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

Сценарий 2: Доступ к конфиденциальным страницам путем знания/угадывания URL-адреса

Еще одной распространенной ошибкой является неспособность проверить, имеет ли пользователь доступ к странице или нет. Существует несколько вариантов этого, либо из-за неспособности проверить, доступна ли страница для определенной роли, либо из-за неспособности проверить видимость страницы. Мы рассмотрим здесь оба случая.

На этот раз мы будем иметь дело с презентационным сайтом компании, и они собираются запустить новый продукт. Редакторы пишут необходимые материалы, готовят страницу нового продукта и ждут, когда продукт будет показан через несколько дней, чтобы они могли опубликовать страницу. Все готово, включая окончательный URL-адрес страницы нового продукта, так как его нужно было заранее отправить в новостные агентства.

Одна из таких торговых точек не была включена в первоначальный пресс-релиз, но, поскольку это известная компания и ходят слухи о новом продукте, они также хотят получить всю пикантную информацию. Поэтому они начинают пытаться угадать URL-адрес страницы нового продукта. Они знают название продукта и, основываясь на переданных продуктах, имеют представление о том, как может выглядеть URL-адрес новой страницы. Поскольку CMS не может подтвердить, была ли статья действительно опубликована или нет, получив доступ к URL-адресу, новостное издание получает всю необходимую информацию и может свободно публиковать ее быстрее, чем любой другой новостной сайт.

Вариация этого может произойти и в сложных системах. Нередко некоторые функции или страницы сайта оказываются недоступными, если у пользователя нет нужной роли. Разработчик составил список страниц, которые видны для каждого пользователя, и на основе этого списка и текущего пользователя, вошедшего в систему, он создает меню в HTML-файле. Это делается аналогично форме выбора из первого сценария. Если разработчик предполагает, что пользователь будет перемещаться только по пунктам меню в пользовательском интерфейсе, он может контролировать проверку на стороне сервера. Таким образом, пользователь может получить доступ к странице, на которую он не должен заходить, просто введя правильный URL-адрес. Эта проблема довольно часто встречается в веб-приложениях, которые еще не созрели или находятся в стадии разработки, но уже выпущена общедоступная версия.

Для другого примера вы можете прочитать мою оригинальную статью на Почему вы всегда должны выполнять проверку на стороне сервера

Оригинал: “https://www.codementor.io/@petrepopescu/you-should-always-do-server-side-validation-always-1kqgtlaprb”