Безопасные вызовы API с AJAX и PHP для 3rd party API

Я хочу сделать GET, POST & PUT вызовы 3rd party API и отобразить ответ на стороне клиента через AJAX. Вызовы API требуют токена, но мне нужно сохранить этот токен в секрете / не в клиентском JS-коде.

Я видел несколько предложения, как этот иметь серверный код посередине, который будет запрашиваться AJAX, и будет обрабатывать фактический вызов API. Я в порядке, работая непосредственно с API из AJAX, но я не уверен, как работать с двухэтапный процесс, чтобы скрыть токен от пользователей. Мой Googling не нашел никаких указателей на лучший метод достижения этого.

в моем случае сервер в середине будет работать PHP, поэтому я предполагаю, что cURL / Guzzle-это простой вариант для выполнения вызовов API с токеном. Ответы API будут JSON.

может кто-нибудь, пожалуйста, дайте мне пример того, как это будет достигнуто с помощью jQuery.ajax (), на PHP, на 3-ю сторону API-интерфейс?

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

редактировать
Вероятно, стоит отметить, что я хочу как можно больше гибкости в развертывании этого; он будет использоваться на нескольких сайтах с уникальными конфигурациями, поэтому в идеале это будет реализовано без изменения сервера или учетной записи хостинга конфигурация.

6 ответов


это немного трудно без образца кода. Но как я понял, вы можете следовать этому,

AJAX CALL

$.ajax({
        type: "POST",
        data: {YOU DATA},
        url: "yourUrl/anyFile.php",
        success: function(data){
           // do what you need to 

            }
        });

в PHP

соберите свои опубликованные данные и обработайте API, что-то вроде этого

$data = $_POST['data']; 
// lets say your data something like this
$data =array("line1" => "line1", "line2"=>"line1", "line3" =>"line1");


 $api = new Api();
 $api->PostMyData($data );

пример класса API

class Api
{
const apiUrl         = "https://YourURL/ ";
const targetEndPoint = self::apiUrl. "someOtherPartOFurl/";

const key       = "someKey819f053bb08b795343e0b2ebc75fb66f";
const secret    ="someSecretef8725578667351c9048162810c65d17";

private $autho="";



public function PostMyData($data){      
  $createOrder = $this->callApi("POST", self::targetEndPoint, $data, true);
  return $createOrder;
 }

private function callApi($method, $url, $data=null, $authoRequire = false){
    $curl = curl_init();

    switch ($method)
    {
        case "POST":
            curl_setopt($curl, CURLOPT_POST, 1);

            if ($data)               
                curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
                break;
        case "PUT":
            curl_setopt($curl, CURLOPT_PUT, 1);
            break;
        default:
            if ($data)
                $url = sprintf("%s?%s", $url, http_build_query($data));
    }

    if($authoRequire){
        $this->autho = self::key.":".self::secret;
        // Optional Authentication:
        curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($curl, CURLOPT_USERPWD, $this->autho);
    }

    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($curl);

    curl_close($curl);


    return $result;

 }
}

потому что все, что вы хотите, это добавить токен в http headers, который я предполагаю, является Authorization простым способом было бы реализовать прокси-сервер, который выполняет вызовы вашей конечной точки api после их добавления. Пример файла для nginx будет

location /apiProxy {
    proxy_pass http://www.apiendPoint.com/;
    proxy_set_header Authorization <secret token>;
}

это гораздо более умный подход, а не писать программу и получает вас с 4 строк кода. Обязательно измените параметры и добавьте другие параметры, необходимые для используемого клиента api. Единственный разница на стороне javascript заключается в использовании location url а не один, предоставляемый службой, которая действует как прокси.

редактировать

конфигурация apache будет

NameVirtualHost *
<VirtualHost *>
   <LocationMatch "/apiProxy">
      ProxyPass http://www.apiendPoint.com/
      ProxyPassReverse http://www.apiendPoint.com/
      Header add Authorization "<secret token>"
      RequestHeader set Authorization "<secret token>"   
   </LocationMatch>
</VirtualHost>

из ваших требований похоже, что сценарий ретрансляции(прокси)" серверный код посередине " является лучшим вариантом.

пример PHP здесь. N. B. Для обработки ошибок CURL он возвращает новый "объект", содержащий ['status'] ('OK' или информацию о сбое CURL) и ['msg'], содержащий фактический ответ от поставщика API. В вашем JS исходный "объект" API теперь потребует извлечения одного уровня вниз под "msg".

основные реле / прокси могут быть обошел

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

запуск сценария AJAX / relay поисковыми ботами

Google боты (другие?) выполнить AJAX. Я!--18-->предположим (реле или нет) если ваш AJAX не нуждается в пользовательском вводе, то посещения ботов приведут к использованию ключа API. Боты "улучшаются". В будущем (сейчас?) они могут эмулировать пользовательский ввод, например, если выбор города из раскрывающегося списка приводит к запросу API, то Google может задействовать параметры раскрывающегося списка thro.

если вас беспокоит, вы можете включить проверку в свой сценарий ретрансляции, например

  $bots = array('bot','slurp','crawl','spider','curl','facebook','fetch','mediapartners','scan','google'); // add your own
  foreach ($bots as $bot) :
    if (strpos( strtolower($_SERVER['HTTP_USER_AGENT']), $bot) !== FALSE):  // its a BOT
      // exit error msg or default content for search indexing (in a format expected by your JS)  
      exit (json_encode(array('status'=>"bot")));
    endif;
  endforeach;

сценарий реле и дополнительный код для удовлетворения вышеуказанных проблем

не переусердствовать защита от пиратов; реле должны быть быстрыми и незаметными для посетителей. Возможные решения (нет эксперта и ржавый с сессиями):

1: PHP сессии решение

проверяет, вызывается ли relay кем-то, кто посетил вашу страницу AJAX за последние 15 минут, предоставил действительный токен и имеет тот же агент пользователя и IP-адрес.

Ваши Страницы Ajax добавьте в PHP следующие фрагменты & JS:

  ini_set('session.cookie_httponly', 1 );
  session_start();
  // if expired or a "new" visitor
  if (empty($_SESSION['expire']) || $_SESSION['expire'] < time()) $_SESSION['token'] = md5('xyz' . uniqid(microtime())); // create token (fast/sufficient) 

  $_SESSION['expire'] = time() + 900; // make session valid for next 15 mins
  $_SESSION['visitid'] = $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'];
  ...
  // remove API key from your AJAX and add token value to JS e.g.
  $.ajax({type:"POST", url:"/path/relay.php",data: yourQueryParams + "&token=<?php echo $_SESSION['token']; ?>", success: function(data){doResult(data);} });

сценарий реле/прокси (версия сеанса):

использовать существующий пример сценария ретрансляции и перед блоком CURL добавить:

  session_start();  // CHECK REQUEST IS FROM YOU AJAX PAGE
  if (empty($_SESSION['token']) ||  $_SESSION['token'] != $_POST['token'] || $_SESSION['expire'] < time()
        || $_SESSION['visitid'] != $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']  ) {
    session_destroy();  // (invalid) clear session's variables, you could also kill session/cookie
    exit (json_encode(array('status'=>'blocked'))); // exit an object that can be understood by your JS
  }

предполагает стандартные настройки ini сеанса. Требуется куки-файлы и страница / ретрансляция в одном домене (возможно workround). Сеансы могут повлиять на производительность. Если сайт уже использует сеансы, код должен будет учитывать это.

2: без сеансов/без опции

использует маркер, связанный с определенным IP-адресом и агентом пользователя, действительный не более 2 часов.

функции, используемые как page и relay например, " функции сайта.inc":

<?php
function getToken($thisHour = TRUE) {  // provides token to insert on page or to compare with the one from page
  if ($thisHour) $theHour = date("jH"); else $theHour = date("jH", time() -3600); // token for current or previous hour
  return hash('sha256', 'salt' . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] .  $theHour); 
}

function isValidToken($token) {  // is token valid for current or previous hour
  return (getToken() == $token || getToken(FALSE) == $token);
}
?>

Реле Скрипт использовать существующий пример и перед блоком CURL добавить:

// assign post variable 'token' to $token 
include '/pathTo/' . 'site-functions.inc';
$result = array('status'=>'timed out (try reloading) or invalid request');
    if ( ! isValidToken($token)) exit(json_encode(array('msg'=>'invalid/timeout'))); // in format for handling by your JS

страниц, нуждающихся в API (или javascript включает файл):

<?php include '/pathTo/' . 'site-functions.inc'; ?>
...
// example Javascript with PHP insertion of token value
var dataString = existingDataString + "&token=" + "<?php echo getToken(); ?>"
jQuery.ajax({type:"POST", url:"/whatever/myrelay.php",data: dataString, success: function(data){myOutput(data);} });

Примечание: пользовательский агент поддельный. IP (REMOTE_ADDR)" не может " быть подделан, но настройка на меньшинстве сайтов может вызвать проблемы, например, если вы находитесь за NGINX, вы можете найти REMOTE_ADDR всегда содержит IP-адрес сервера NGINX.

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


как указывали люди, Вы хотите, чтобы прокси-метод на вашем сервере скрывал API-ключ.

чтобы избежать неправильного использования вашего метода на сервере, защитите вызов с помощью одноразового маркера (как вы обычно используете для форм), сгенерированного с вашего сервера (не в javascript..).

Я не поклонник закодированных вставленных выше, которые проверяют известные агенты http-пользователей... или токены сайта ... это небезопасно.


Если вы используете cUrl, который вы должны защитить, это ваш сервер. Способ, которым я лично использую, - это Google reCaptcha, который уверен, что сделан для организации таких проблем, как ваши. Очень хорошо объяснил интеграцию в клиентской и серверной сторонах шаг за шагом здесь. https://webdesign.tutsplus.com/tutorials/how-to-integrate-no-captcha-recaptcha-in-your-website--cms-23024 Таким образом, вам не нужно ничего менять в ваших файлах virtualhost и любых конфигурациях apache.


Я хотел бы использовать solutiuon @MMRahman опубликован, если вы хотите, чтобы добавить слой безопасности между серверной и веб-интерфейсе, что вы могли бы сделать это, когда пользователь делает логин генерировать уникальный идентификатор, сохранить его на сервере сессии и в куки или локальное/сессии хранить в браузере, таким образом, когда вы называете ваш сервер с помощью AJAX вы можете получить значение из места, где хранятся в браузере, и проверить, если значения совпадают, если да, вы вызов внешнего API и вернуть значения, если не просто игнорировать запрос.

Так summaring: Логин пользователя -> генерировать уникальный идентификатор -> хранить его в сессии сервера и браузера -> сделать звонок от Аякса передав в качестве параметра значение из браузера-> проверить, если он вышлет с сервером сессии сохраненное значение -> если да вызов внешнего API с помощью токена, хранится в серверной / БД / файл / что угодно