Зачем солить HTTP-коллбэки


    Некоторые облачные сервисы хотят двустороннего общения для нотификаций: рассказать вашему backend о завершении долгой операции, показать случившиеся ошибки, предупредить о низком балансе платных услуг — вся вот эта история. И если для общения с сервисами мы привыкли использовать HTTP-запросы, то в обратную сторону есть много вариантов: от проверок статуса раз в десять минут и до постоянного WebSocket или HTTP/2 подключения с нотификациями в реальном времени. Самый простой способ это HTTP callbacks. Вы задаете в админке URL своего бэкенда, а облачный сервис в случае интересных событий делает HTTP-запрос к этому URL с дополнительный информацией в теле запроса. Обратная сторона простоты это безопасность. Как убедиться, что запрос сделал именно облачный сервис, а не злобный хакер Вася? Несколько способов под катом.

    Передача и проверка токена


    Самый простой способ внутри самого простого способа: в админке вы задаете или генерируете токен (например, GUID), который облачный сервис передает в каждом запросе. А на стороне своего бэкенда просто проверяете этот токен.

    Очевидный минус: если злоумышленник подслушает трафик между вашими сервисами, то он узнает токен и сможет делать запросы от имени сервиса. От этого хорошо защищает HTTPS, но есть нюансы. Например, трафик между серверами может не шифроваться для ускорения работы. Или вы можете отлаживать ваш бэкенд на локальной машине, сидя в Старбакс без VPN. Звучит дико, но в жизни всякое бывает.

    HTTP-авторизация на стороне вашего бэкенда


    От токена отличается только тем, что проверка переносится из вашего кода на уровень веб-сервера, который вы используете для обработки запросов. Задаете логин плюс пароль в nginx/expressjs/Django/Rails/whatever, их же в админке облачного сервиса, после чего все проверки делаются автоматически. Но подслушать логин с паролем можно точно так же, а HTTPS имеет неприятную особенность отключаться «на минутку мне только локально отладить».

    Фильтрация по IP


    Если запросы по URL для http callback разрешить только с определенного IP-адреса, то у хакера будут большие проблемы такой запрос организовать. Не то чтобы это было невозможно (если ответ не требуется, то нужную последовательность TCP-пакетов можно и организовать), но технически трудно. Засада тут в другом — у облачного сервиса может быть много IP-адресов, с которых он отправляет запросы, что делает такую защиту слишком ненадежной в простой реализации и слишком сложной, если нужна надежность.

    MD5-подпись с солью


    Хороший сбалансированный способ с общим секретом, который мы используем в Voximplant. В админке задается произвольная текстовая строка («криптографическая соль», в роли которой хорошо подходит GUID), после чего для каждого запроса облако Voximplant считает MD5-хэш от данных аккаунта, запроса и соли. На стороне бэкенда, зная соль, можно также посчитать хэш и сравнить с указанным в запросе. Если совпало — значит все хорошо. А если нет, то можно принимать поздравления в создании действительно популярного сервиса, на который даже хакеры заходят.



    Еще способы?


    Я рассказал только несколько простых способов защиты, которые используются на практике в HTTP API крупных проектов. Видели что-то еще интересное? Делитесь в комментариях, буду признателен!
    Voximplant 184,67
    Облачная телеком-платформа
    Поделиться публикацией
    Комментарии 31
    • +1
      Разве MD5 уже не считается устарелым и небезопасным? Сейчас вроде в моде SHA семейство.
      • 0
        Там есть несколько атак. Можно забрутфорсить хешированную строку, если она короткая. Но у нас она длинная. Можно сделать MD5 от 'foobar' не зная 'foo'. Не поможет, у нас MD5 от конкретной длинной строки. Можно сделать другую последовательность байт, которая будет иметь тот же MD5 хэш — но в случае JSON payload это приведет к невалидным данным. Как-то так.
        • +1
          Можно сделать другую последовательность байт, которая будет иметь тот же MD5 хэш — но в случае JSON payload это приведет к невалидным данным.

          Кто сказал?

            • –1

              Да, я согласен с заголовком.


              И где там про, что для дьявола и ангела есть коллизии, а для JSON нет? И насколько авторитетный это источник, чтобы подкреплять им свою (и клиентов) безопасность? Отдайте безопасность людям, которые в этом разбираются, как минимум в стандартах и хороших практиках. А то потом на API смотреть больно.

          • +1
            Вобще в теории колизия делается легко.
            например валидный json.
            {"action": {
              "id": "file",
              "value": "File"}}

            Хотим сделать что-то другое.
            {"action": {
              "id": "eval",
              "value": "some_code"},"somesting":"some_data_with_32bytes"}

            И в теории мы можем перебирать some_data_with_32bytes и можно перебирать пока не совпадет хеш. И вот вам валидный json на который сервер даже не отреагирует никак.
            • 0
              По той атаке что по ссылке оно в конец дописывается. А в конце у JSON закрывающие скобочки и все вот это. Коллизию с произвольными данными, насколько я знаю, простым способом сделать нельзя. Или я ошибаюсь?
              • +1
                И в теории мы можем перебирать some_data_with_32bytes и можно перебирать пока не совпадет хеш

                Долго и дорого.

          • +5
            Велосипеды — это модно, спортивно, молодёжно!
            • 0
              Почему велосипеды? Один из вполне стандартных методов, которые используют облачные платформы в качестве дополнительной страховки к HTTPS. А какое решение вы считаете не велосипедом? :)
              • 0

                jwt

                • 0

                  От какого типа атаки вас это защищает? От MITM — не думаю, что это безопаснее чем TLS?
                  Может лучше проверку клиентского сертификата тогда сделать?

                  • 0
                    От того, что кто-то сделает HTTP запрос к backend нашего клиента «от имени» Voximplant. Как я писал в статье, есть много разных способов. Этот хороший по соотношению потраченных разработчиком усилий (минута и пара строк кода) и степени защиты (простого способа обойти нет).
                  • 0
                    1. обычно используют все же не накополенную поделку, а стандартизированный hmac.
                    2. md5 уже не считается безопасным
                  • +2

                    Притом велосипеды с какой-то странной терминологией. Разве ж это соль в общепринятом понимании? Это скорее private key.

                    • 0
                      Это именно соль. Чтобы нельзя было сделать фейковый коллбек, зная только его данные и данные аккаунта
                      • +1

                        Не согласен.


                        In cryptography, a salt is random data that is used as an additional input to a one-way function that "hashes" data, a password or passphrase.

                        Соль не подразумевает секретность. Это дополнительное значение, которое подмешивается в хэшируемые данные. И оно должно быть рандомным и различным для каждого хэшируемого значения. А у вас все время подмешивается одно и то же значение, и подразумевается, что оно никому не известно.

                        • +2
                          Это не соль, а общий ключ (shared key)
                    • 0
                      облако Voximplant считает MD5-хэш от данных аккаунта, запроса и соли

                      Для таких вещей вроде как hash_hmac() существует.
                    • 0
                      старый добрый hmac, только в профиль
                      • +1
                        а полученный hash(account + request body + salt) вы как передаете, в url (header)?
                        • 0
                          Нет, в самом JSON
                          • 0
                            так ведь hash зависит от тела запроса, или я не так понял?
                            • 0

                              На php код примерно такой:


                              $key = 'pUWTzQ3qGNgIn39nTI4ul3BfS9v5UHdioz+ao8AKjxw=';
                              $data = json_decode(stream_get_contents(STDIN), true);
                              $hmac = $data['hmac'];
                              unset($data['hmac']);
                              if ($hmac !== hash_hmac('sha256', json_encode($data), $key)) {
                                throw new \RuntimeException('Bad HMAC');
                              }
                              • 0
                                Не от тела целиком, от одного из полей с id запроса
                                • +2

                                  Если только от id, то как это защищает от подмены тела?

                          • –1
                            JWT

                            Все.
                            • +2
                              Даже без рассмотрения полноценной инфраструктуры ЭЦП безопасней использовать полноценную подпись с закрытым и открытым ключом. В админке облака виден открытый ключ, который бэкенд может спокойно хранить в открытом виде, а в самом облаке где-то есть закрытый, который даже пользователь админки не знает. То есть даже взлом админки (компроментация пользователя) не позволит никому от имени облака слать запросы на бэкенд.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое