Создание PHP API: проверка, с какого сервера запрашивается запрос API

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

  • API настроен на api.example.com.
  • Пользователь, который хочет использовать регистры API у нас, добавляет свой домен и получает ключ API.
  • Пользователь API будет использовать свой ключ API для шифрования своих данных запроса (через mcrypt) и отправит его через cURL в api.example.com.
  • Мой сервер проверяет, из какого домена этот запрос API приходит от, и сопоставляет этот домен с ключом API в базе данных. Если есть ключ API, API расшифровывает запрос через mcrypt с этим ключом, а затем используя тот же метод шифрует и отправляет результат.

Я застрял на шаге 4. Первоначально я планировал использовать HTTP_REFERER, чтобы проверить его, но поскольку cURL не отправляет его по умолчанию, и его можно легко подделать в пользовательском коде (CURLOPT_REFERER, насколько я помните), я застрял здесь.

Есть ли способ узнать, из какого домена поступает этот запрос API? Я вижу, что это можно сделать с помощью некоторых популярных API, таких как reCAPTCHA. Проверка _SERVER [ "REMOTE_HOST" ] на самом деле не является вариантом из-за общих хостов (они имеют одинаковые IP-адреса), поэтому это не сможет предотвратить злоупотребления (которые в большинстве случаев будут происходить с общих серверов).

Есть ли такой метод для проверки? Спасибо!

Ответ 1

@Shafee имеет хорошую идею, он просто нуждается в некоторой настройке. Мы фокусируемся на видимой части вызова API, который является ключом API. Это видно в URL-адресе и сообщает API, который запрашивает данные. Вместо того, чтобы пытаться помешать другим воровать этот ключ и запускать свой собственный вызов cURL с доменом, из которого они его перехватили, мы можем "просто добавить" еще один ключ в микс, который не виден этим перехватчикам. Я не говорю о том, чтобы остановить проверку, откуда приходит запрос, но по-прежнему является хорошим способом вывести недопустимые запросы на раннем этапе в script, но со вторым ключом вы гарантируете, что только человек, запрашивающий данные, действительно знает, как чтобы получить данные (вы доверяете им не отдавать их кому-либо).

Итак, когда пользователь регистрируется для ключа, вы фактически назначаете пользователю два разных ключа.
API_KEY. Открытый ключ, который соединяет вас с вашим доменом. Система ищет домен и ключ, предоставленные для поиска следующего ключа.
MCRYPT_KEY. Это ключ, который будет использоваться для шифрования данных через Mcrypt. Поскольку это зашифрованные данные, только реквестер и сервер будут знать, что это такое. Вы используете ключ для шифрования данных и отправки зашифрованного ввода с помощью ключа API на сервер, который находит ключ, необходимый для дешифрования этого ввода с помощью ключа API и домена (и IP), которые были предоставлены. Если они не зашифровали данные надлежащим ключом, то дешифрование с помощью правильного ключа вернет тарабарщину, а вызов json_decode() вернет NULL, позволяя script просто вернуть ответ "invalid_input".

В конечном итоге с помощью этого метода нам даже нужно проверить, где (домен/IP) запрашивается запрос? Используя этот метод, пользователи API не отказываются от пары ключей API/MCRYPT для других пользователей, подобно тому, как не выдают ваше имя пользователя/пароль. Тем не менее, любой веб-сайт может легко просто зарегистрироваться, чтобы получить свою собственную пару ключей и использовать API. Также следует отметить, что API даже не вернет ничего полезного на свой сервер, если пользователь на своем конце не войдет в систему с использованием правильного имени пользователя и пароля, поэтому их конец уже будет иметь эту информацию. Единственное, что новый наш сервер действительно возвращает, - это адрес электронной почты при успешной проверке пользователя. Сказав это, нам даже нужно использовать cURL? Не могли бы мы просто использовать file_get_contents('http://api.example.com/{$API_KEY}/{$MCRYPT_DATA}')? Я понимаю, что задаю больше вопросов в своем ответе...

Ответ 2

Вы можете изменить, из какого ip-запроса приходит запрос, и вы можете выполнить поиск ptr, чтобы получить доменное имя для этого ip, но, скорее всего, у ip-адреса больше одного домена, и вы в конечном итоге ошибаетесь, поэтому я рекомендую, чтобы клиент отправил свое доменное имя в запросах, возможно, с HTTP_REFERER, и что вы делаете проверку DNS, если этот домен указывает на ip, запрашивая его, но обратите внимание, что домен, например google.com, может указывать на большее количество то один ip. (ip может быть сфальсифицирован, с некоторыми хорошими навыками хакерства, но это из моих знаний)

Ответ 3

Как насчет введения второй переменной, например let app id. Когда пользователь регистрирует свой домен, свяжите этот идентификатор с доменом. Пользователь должен отправить app id с каждым запросом без шифрования вместе с зашифрованным вызовом api. Затем вы можете найти app id получить app secret и попытаться расшифровать?

Ответ 4

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

Кроме того, что, если кто-то решит реализовать настольное приложение с помощью вашего API? Разумеется, они не будут требовать, чтобы их пользователи отправляли им свои IP-адреса, чтобы они могли их белыми списками?

Кроме того, вы можете комбинировать предельные скорости/ограничения запросов и ограничивать скорость в зависимости от количества запросов, например, как Verizon ограничивает скорость своей сети 3G, если вы передаете определенный объем использования данных.