Защита конечной точки API RESTful без сеанса

Я создал простой RESTful API для проекта, частично следуя этому очень хорошему сообщению в блоге Рияда Каллы. Теперь я прочитал десятки похожих вопросов о переполнении стека, но не могу найти ответ на свой секретный вопрос.

Вкратце, мои запросы выглядят так:

  1. У клиента есть открытый ключ API (открытый текст, доступный любому, кто анализирует сетевой трафик или правильно проверяет источник кода)
  2. Клиент отправляет запрос на сервер с открытым ключом API
  3. На сервере есть секретный ключ API (секретный для всех, кроме разработчика)
  4. Сервер создает хеш HMAC-SHA1, состоящий из данных запроса клиента и секретного ключа API.
  5. Сервер отправляет запрос, идентичный запросу клиента, на сервер API, но включая полученный HMAC-SHA1
  6. Сервер API ищет секретный ключ API в своей базе данных на основе открытого ключа API, который он получает.
  7. Сервер API воссоздает хеш HMAC-SHA1, используя те же данные, что и сервер разработчика
  8. Если хэши совпадают, запрос считается действительным и обрабатывается нормально

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

Я постараюсь привести конкретный пример. Обычно я бы сделал:

  1. AJAX получить запрос на мой сервер.
  2. Мой сервер хеширует мой запрос с моим секретом API и отправляет его на сервер API.
  3. Сервер API проверяет мой запрос и возвращает полезную нагрузку.

Но я боюсь, что

  1. Доктор Зло вынюхивает мой открытый ключ API.
  2. Доктор Зло будет отбирать запрос на получение моего сервера, используя мой открытый ключ API.
  3. Мой сервер хэширует запрос доктора Зла с моим секретом API и отправляет его на сервер API.
  4. Сервер API проверяет и возвращает полезную нагрузку, чтобы завершить порочные планы доктора Зла.
  5. Доктор Зло смеется злой смех.

Что-то я пропускаю или это просто часть 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. Однако есть две дополнительные вещи, которые сделают ваше приложение более безопасным.

  1. Требовать отметку времени в POST клиента, необходимо проверить в течение 5 минут времени сервера. Он также должен быть включен в поколение HMAC. Если кто-то попытается изменить это, подпись HMAC будет недействительной, если у него не будет секретного ключа для обновления подписи HMAC.
  2. Используйте SSL с проверкой сертификата. Предотвращает человека в середине атаки. Не разрешать любые не-ssl запросы.
Другие вопросы по тегам