Владимир Зарыпов (krre31) wrote,
Владимир Зарыпов
krre31

Category:

Океан. Токен

В прошлый раз я писал, что меня от маленького шага человека и большого скачка для всего человечества в развитии сайта останавливают две вещи - отсутствие TypeScript и SSL. Вроде бы всё это уже добавлено, так за чем же дело стало? Ну, на самом деле я вас обманул. Не хватало не двух вещей, а трёх. Третья - это защитить API-сервер от посягательств кулхацкеров. И пока это не было сделано, я не мог спокойно спать. Просыпался среди ночи и терзал себя тревожными мыслями, не взломал ли кто API-сервер? Объясню, в чём суть.

В древние времена, когда по земле ещё бродили динозавры, для работы сайта достаточно было одного веб-сервера, чаще всего какого-нибудь Apache. При помощи Perl, PHP, Ruby или какого-то другого модного языка программирования этот сервер обращался к базе и выдавал данные, перемешанные с HTML-кодом. Сейчас так делают всё реже, потому что появилась масса сервисов, специализированных только на выдаче данных, а кто и как их будет показывать у себя на сайте, это уже не их дело. Классический пример - Google, в котором куда ни сунься, всюду наткнёшься на какое-нибудь API. Это хороший прибыльный бизнес, поэтому появилась несчётное количество сервисов, которые за баблишко или иногда даже просто так отдают какие-нибудь данные. Это и называется API-сервер, то есть этакая хитрожопая разновидность бэкенда. Веб-серверы тоже остались, но претерпели существенные изменения. Теперь это обычно не Apache, а какая-нибудь Node.js, заточенная под разработку фронтенда на JavaScript или TypeScript.

Чтобы не отставать от жизни, я пошёл точно таким же путём. Каркас страницы отображаю при помощи веб-сервера Express, работающего на Node.js, а данные заполняю запросами к API-серверу, который написал на Rust. Стандарта, на чём писать бэкенд, никакого нет, поэтому каждый в этом деле извращается, насколько хватит его фантазии. Насколько извращённой оказалась моя фантазия, вы уже поняли :))

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

Первым делом я убрал позорную отправку на сервер пароля в незашифрованном виде. Вместо обычного текста теперь отправляется зашифрованная в SHA-1 строка. Правда теперь уже пишут, что шифрование в SHA-1 устарело, и надо пользоваться SHA-2 или SHA-256, но разве за этими стандартами уследишь? Потом когда-нибудь переделаю.

Этого мне показалось мало, и я зашифровал вообще весь трафик при помощи SSL - как между браузером и веб-сервером, так и между веб-сервером и API-сервером. Казалось бы, пароли можно теперь вообще не шифровать, ведь их всё равно больше никто не увидит, но это мнение ошибочно. Каждый запрос к серверу нужно логировать в целях изучения возможных ошибок, и вот там-то пароль и вылезет в своём первозданном виде. А паролей пользователей программисты знать не должны, потому что абсолютно честных людей не бывает, и даже самому себе доверять нельзя :))

Всё, что было до этого - это самая лёгкая часть в настройке безопасности. Настоящий ад начинается дальше, когда приходит очередь защитить API. В чём проблема? Я сделал API с набором методов для всех необходимых операций по обработке данных на сайте, включая те, которые доступны только администратору, наподобие удаления мандел. Однако API сервер не сортирует тех, кто обращается к нему с запросами. Любой деревенский валенок, зная, какие методы есть в API, может запускать их со своей консоли и делать с данными что угодно. Например, поменять кому-нибудь пароль, удалить чей-нибудь коммент или проголосовать за манделы, даже не будучи зарегистрированным. Ситуация хуже не придумаешь. Решения у такой проблемы два, и оба обязательны - аутентификация (определение личности) и авторизация (предоставление прав).

Чтобы определить, от чьего имени делается запрос, я отправлял в каждом запросе параметр user_id. Быстрый и суровый способ, но совершенно никакущий. Мало того, что API замусоривается бесконечными user_id, так ещё и взломщику не составит никакого труда подставить туда любой id, скажем, мой, и развлекаться потом, сколько душе угодно. В качестве разумной альтернативы вместе с каждым запросом нужно отправлять токен, который создаётся при регистрации пользователя и впоследствии однозначно его идентифицирует. Токен зашивается в HTTP-заголовок запроса или в URL, и по нему сервер безошибочно определяет, кто отправил запрос. Это называется аутентификация. Чтобы её сделать, мне понадобилась неделя. Пришлось и кэш для этих токенов создавать, и user_id везде вычищать, и переделывать почти половину API. Даже для лютых конспирологов я выделил отдельный токен, общий на всех.

Вы думаете, что уже всё? Нет, далеко не всё! Теперь представим ситуацию, что кулхацкер раздобыл какой-нибудь токен. Можно даже без регистрации банально подсмотреть в браузере в режиме разработчика токен лютого конспиролога или всё-таки зарегистрироваться и воспользоваться своим собственным. Подставив токен в запрос, мы обнаружим, что сервер спокойно допускает нас к выполнению методов, но опять же, совершенно любых! Он не знает, кому что можно, а что нельзя. Надо прописать правила, какая группа пользователей или отдельные пользователи могут выполнять одни методы и не могут другие. Это называется авторизация.

В итоге получается такая последовательность действий. Браузер отправляет API-серверу запрос с указанием токена и метода, который нужно вызвать. Сервер по токену определяет личность пользователя, его id в базе, затем по этому id проверяет, к какой группе пользователей он относится. В этой группе смотрит права и затем решает, выполнять метод или вернуть ошибку нарушения прав.

Демонстрирую на примере, как это работает, чтобы не говорили, что у меня скучные статьи без картинок.

Предположим, что какой-нибудь абстрактный кулхацкер решил взломать наш сервер и удалить с него манделу с номером 666 (о Дубровском), потому что недавно вступил в православие и отныне на дух не переносит это число. Он даже знает, какое у сервера API, хотя я его нигде не публиковал. Для начала он пытается выполнить запрос в лоб, без указания токена.


Иди гуляй - Bad request. Без токена это вообще не запрос по мнению сервера. Ладно, кулхацкер подставляет какой-то токен и снова пытается удалить манделу.


Снова иди гуляй - Unathorized. Такого токена нет в базе, и для сервера ты никто. Тогда кулхацкер находит в браузере токен лютого конспиролога и подставляет в запрос.


Forbidden - нет прав на удаление. Ну, а что вообще можно сделать с токеном лютого конспиролога? Пожалуйста, посмотреть манделу. Хотя это всё равно ни для кого не секрет. Любой анонимный пользователь может посмотреть какую угодно манделу на сайте.


Этот запрос выполняется успешно. "Спокойно, Маша, я Дубровский", говорил не говорил Дубровский.

Кажется, теперь я не могу больше придумать, что ещё нужно доработать на сайте, прежде чем продолжать делать новые фичи. Наверное, можно заниматься форумом. Сделать форум - это очень сложная задача, поэтому она потребовала большой предварительной подготовки. Там будет много разных запросов, и чтобы не пришлось их все переделывать потом, я потратил некоторое время для наведения порядка сейчас. Конечно, есть ещё такая больная тема, как дизайн, которого почти нет, и позже, если он появится, тоже придётся много переделывать, но я не хочу об этом думать. От его отсуствия я не просыпаюсь по ночам, и совесть не терзает меня за непрофессионализм, так что переживём :))
Tags: океан
Subscribe

Posts from This Journal “океан” Tag

  • Океан. Хлам

    Добавил перемещение низкорейтинговых мандел в хлам. Это значит, что такие манделы не будут показываться в общем каталоге, но будут видны, если…

  • Океан. Полнотекстовый поиск

    Сделал, наконец, фичу, реализацию которой очень долго откладывал, потому что она сложная, а я животное ленивое и сложностей очень боюсь.…

  • Океан. Лента новостей

    Придумал новую киллер-фичу - ленту новостей. Она объединяет в себя сообщения о появлении новых мандел, комментариев к манделам, новых тем на форуме…

  • Post a new comment

    Error

    default userpic
    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 10 comments

Posts from This Journal “океан” Tag

  • Океан. Хлам

    Добавил перемещение низкорейтинговых мандел в хлам. Это значит, что такие манделы не будут показываться в общем каталоге, но будут видны, если…

  • Океан. Полнотекстовый поиск

    Сделал, наконец, фичу, реализацию которой очень долго откладывал, потому что она сложная, а я животное ленивое и сложностей очень боюсь.…

  • Океан. Лента новостей

    Придумал новую киллер-фичу - ленту новостей. Она объединяет в себя сообщения о появлении новых мандел, комментариев к манделам, новых тем на форуме…