Защита конечной точки API RESTful без сеанса
Я создал простой RESTful API для проекта, частично следуя этому очень хорошему сообщению в блоге Рияда Каллы. Теперь я прочитал десятки похожих вопросов о переполнении стека, но не могу найти ответ на свой секретный вопрос.
Вкратце, мои запросы выглядят так:
- У клиента есть открытый ключ API (открытый текст, доступный любому, кто анализирует сетевой трафик или правильно проверяет источник кода)
- Клиент отправляет запрос на сервер с открытым ключом API
- На сервере есть секретный ключ API (секретный для всех, кроме разработчика)
- Сервер создает хеш HMAC-SHA1, состоящий из данных запроса клиента и секретного ключа API.
- Сервер отправляет запрос, идентичный запросу клиента, на сервер API, но включая полученный HMAC-SHA1
- Сервер API ищет секретный ключ API в своей базе данных на основе открытого ключа API, который он получает.
- Сервер API воссоздает хеш HMAC-SHA1, используя те же данные, что и сервер разработчика
- Если хэши совпадают, запрос считается действительным и обрабатывается нормально
Я обеспокоен тем, что кто-то, использующий мой сервис, может получить открытый ключ API (скажем, перехватывая сетевой трафик), а затем просто отбросить тот же запрос, который клиент первоначально сделал бы через свой браузер, используя AJAX, непосредственно на сервер разработчика. Следовательно, злоумышленник может быть аутентифицирован как законный пользователь и получать доступ к API с помощью другого секретного ключа API.
Я постараюсь привести конкретный пример. Обычно я бы сделал:
- AJAX получить запрос на мой сервер.
- Мой сервер хеширует мой запрос с моим секретом API и отправляет его на сервер API.
- Сервер API проверяет мой запрос и возвращает полезную нагрузку.
Но я боюсь, что
- Доктор Зло вынюхивает мой открытый ключ API.
- Доктор Зло будет отбирать запрос на получение моего сервера, используя мой открытый ключ API.
- Мой сервер хэширует запрос доктора Зла с моим секретом API и отправляет его на сервер API.
- Сервер API проверяет и возвращает полезную нагрузку, чтобы завершить порочные планы доктора Зла.
- Доктор Зло смеется злой смех.
Что-то я пропускаю или это просто часть RESTful API-игры?
ОБНОВЛЕНИЕ: я добровольно опускаю любую форму проверки метки времени, чтобы упростить задачу и сосредоточиться на проблеме аутентификации.
ОБНОВЛЕНИЕ 2: я добавил $_SERVER['HTTP_REFERER']
проверка к процессу. Цель здесь состоит в том, чтобы клиент отправлял реферер вместе с запросом, и он должен соответствовать рефереру, указанному в базе данных на стороне API. К сожалению, HTTP-рефереры могут быть легко подделаны. Это еще один уровень безопасности, но все же не идеальный.
ОБНОВЛЕНИЕ 3: Я изменил свой код на стороне сервера, чтобы установить для реферера удаленный IP-адрес. Это вынуждает каждый запрос, отправленный на мой сервер, который хочет хэшироваться с использованием секретного ключа API, в конечном итоге попадает на сервер API с исходным IP-адресом запроса. Затем этот IP-адрес может быть проверен, и запрос может пройти. Я верю, что пока можно подделать $_SERVER['REMOTE_ADDR']
, но это сложнее, чем подделка $_SERVER['HTTP_REFERER']
... Я все еще не идеален.
ОБНОВЛЕНИЕ 4: Согласно этим сообщениям: Как подделать переменную $_SERVER['REMOTE_ADDR']? и https://serverfault.com/questions/90725/are-ip-addresses-trivial-to-forge, подделка $_SERVER['REMOTE_ADDR']
возможно, хотя и сложно. Однако невозможно получить ответ от фальсифицированного запроса, поскольку вы не контролируете фальшивую сеть. Запрос может быть успешно подтвержден, но его ответ не попадет в руки злоумышленников.
2 ответа
Решение, которое я нашел, чтобы запретить другим сценариям использовать открытый ключ API и отправлять запросы на серверный HHAC-хэширующий скрипт, состоит в том, чтобы отправить идентификационную информацию исходного запросчика вместе с запросом. я использую $_SERVER['REMOTE_ADDR']
определить личность исходного запрашивающего, поскольку его сложнее подделать, а подделка обычно означает, что они не получат ответ.
/* $this as a class that handles requests */
// Build hash and include timestamp
$this->vars['timestamp'] = time();
$this->vars['hash'] = hash_hmac('sha1', http_build_query($this->vars).$this->vars['token'], API_SECRET);
// Send request to API
curl_setopt_array($this->curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $url,
CURLOPT_POST => $this->method == 'post' ? 1 : NULL,
CURLOPT_POSTFIELDS => $this->method == 'post' ? $this->vars : NULL,
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 15,
CURLOPT_REFERER => $_SERVER['REMOTE_ADDR'], // Referer here!
CURLOPT_MAXREDIRS => 3,
CURLOPT_HTTPGET => $this->method == 'get' ? true : false
));
После отправки API не только проверяет секретный ключ API из базы данных, но и проверяет, $_SERVER['HTTP_REFERER']
указан как разрешенный! Это также позволяет API принимать серверы для каждого пользователя.
Вы на правильном пути, используя HMAC. Однако есть две дополнительные вещи, которые сделают ваше приложение более безопасным.
- Требовать отметку времени в POST клиента, необходимо проверить в течение 5 минут времени сервера. Он также должен быть включен в поколение HMAC. Если кто-то попытается изменить это, подпись HMAC будет недействительной, если у него не будет секретного ключа для обновления подписи HMAC.
- Используйте SSL с проверкой сертификата. Предотвращает человека в середине атаки. Не разрешать любые не-ssl запросы.