wake-up-neo.net

Sichere API-Aufrufe mit AJAX und PHP zu einer Drittanbieter-API

Ich möchte GET, POST & PUT-Aufrufe an eine Drittanbieter-API vornehmen und die Antwort auf der Clientseite über AJAX anzeigen. Die API-Aufrufe erfordern ein Token, aber ich muss dieses Token geheim halten und nicht im clientseitigen JS-Code.

Ich habe ein paar Vorschläge wie diese gesehen Server-seitigen Code in der Mitte zu haben, der vom AJAX abgefragt würde und den eigentlichen API-Aufruf handhaben würde. Ich bin in Ordnung, direkt mit der API von AJAX zu arbeiten, aber ich bin nicht sicher, wie man mit einem zweistufigen Prozess arbeitet, um das Token vor Benutzern zu verbergen. Mein Googling hat keine Hinweise auf eine Best-Practice-Methode gefunden, um dies zu erreichen.

In meinem Fall würde der Server in der Mitte PHP ausführen, daher gehe ich davon aus, dass cURL/Guzzle die unkomplizierte Option ist, um die API-Aufrufe mit dem Token durchzuführen. Die API-Antworten sind JSON.

Kann mir bitte jemand ein grobes Beispiel geben, wie dies mit jQuery.ajax (), PHP und der Drittanbieter-API erreicht werden könnte?

Wenn es jedoch Qualitätsquellen gibt, die diese Methode ausführlich behandeln, würde ich mich über einen Link freuen. Wenn dies eine schreckliche Methode ist, wäre es gut zu wissen, warum.

Bearbeiten
Wahrscheinlich ist es erwähnenswert, dass ich so viel Flexibilität bei der Bereitstellung wie möglich haben möchte. Es würde an mehreren Standorten mit eindeutigen Konfigurationen verwendet. Idealerweise würde dies ohne Änderung der Server- oder Hosting-Konto-Konfiguration implementiert werden.

8
t-jam

Ohne Beispielcode ist es etwas hart. Aber wie ich es verstanden habe, können Sie dem folgen.

AJAX CALL

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

            }
        });

In PHP

Sammeln Sie Ihre veröffentlichten Daten und verwenden Sie die API. So etwas 

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


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

Beispiel-API-Klasse

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;

 }
}
6
MMRahman

Denn alles, was Sie wollen, ist das Hinzufügen eines Tokens zu http headers, von dem ich annehme, dass es sich um eine Authorization-Methode handelt. Eine einfache Möglichkeit wäre die Implementierung eines Proxy-Servers, der Aufrufe an Ihren API-Endpunkt durchführt, nachdem diese addiert wurden. Eine Beispieldatei für nginx wäre

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

Dies ist ein viel intelligenterer Ansatz als ein Programm zu schreiben und bringt Sie mit 4 Zeilen Code aus. Stellen Sie sicher, dass Sie Ihre Parameter entsprechend ändern und andere Parameter hinzufügen, die von dem von Ihnen verwendeten API-Client benötigt werden. Der einzige Unterschied auf der Javascript-Seite besteht darin, den location url zu verwenden, anstatt einen Dienst, der von einem Dienst bereitgestellt wird, der als Proxy fungiert.

Bearbeiten

Die Konfiguration für Apache wäre

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>
8
georoot

Nach Ihren Anforderungen sieht es so aus, als sei "serverseitiger Code in der Mitte" das Relay (Proxy) -Skript die beste Option. 

PHP Beispiel hier . N.B. Um mit CURL-Fehlern umzugehen, wird ein neues "Objekt" zurückgegeben, das aus ['status'] ('OK' oder Info über CURL-Fehler) und ['msg'] mit der tatsächlichen Antwort des API-Providers besteht. In Ihrem JS müsste das ursprüngliche API "object" jetzt eine Ebene unter "msg" extrahiert werden.

Basisrelais/Proxies können umgangen werden

Wenn Sie ein Relay-Skript verwenden, versucht jemand, der nach einem API-Schlüssel sucht, wahrscheinlich an anderer Stelle. Jedoch; Der Pirat könnte seinen Aufruf an den API-Anbieter einfach mit Ihrem API-Schlüssel durch einen Aufruf an Ihr Skript ersetzen (und Ihr API-Schlüssel wird weiterhin verwendet).

Ausführen Ihres AJAX/Relay-Skripts durch Suchmaschinen-Bots

Google Bots (andere?) Führen AJAX aus. I Annahme (Weiterleitung oder nicht) Wenn für Ihre AJAX keine Benutzereingaben erforderlich sind, führt der Besuch von Bots zu einer Verwendung des API-Schlüssels. Bots "verbessern" sich. In Zukunft (jetzt?) Können sie Benutzereingaben nachahmen, z. Wenn die Auswahl einer Stadt aus einer Dropdown-Liste zu einer API-Anforderung führt, kann Google die Dropdown-Optionen durchlaufen lassen. 

Wenn Sie Bedenken haben, können Sie ein Häkchen in Ihr Relay-Skript setzen, z.

  $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;

Relay-Skript und zusätzlicher Code für die oben genannten Probleme

Übertreiben Sie den Piratenschutz nicht. Relais sollten schnell sein und von Besuchern nicht wahrgenommen werden. Mögliche Lösungen (kein Experte und rostig bei Sitzungen):

1: PHP Session-Lösung

Überprüft, ob das Relais von jemandem aufgerufen wurde, der Ihre AJAX - Seite in den letzten 15 Minuten besucht hat, ein gültiges Token angegeben hat und denselben Benutzeragenten und dieselbe IP-Adresse hat. 

Ihre Ajax-Seiten fügen Ihrer PHP & JS die folgenden Ausschnitte hinzu:

  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);} });

Das Relay/Proxy-Skript (Sitzungsversion):

Verwenden Sie ein bestehendes Beispiel-Relay-Skript und fügen Sie vor dem CURL-Block Folgendes hinzu:

  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
  }

Übernimmt die Einstellungen der Standardsitzung ini. Erforderliche Cookies und Seite/Relay auf derselben Domain (Workround möglich). Sitzungen können sich auf die Leistung auswirken. Wenn die Site bereits Sitzungen verwendet, muss der Code dies berücksichtigen.

2: Sessionless/Cookieless-Option

Verwendet ein Token, das einer bestimmten IP-Adresse und einem bestimmten Benutzeragenten zugeordnet ist und maximal 2 Stunden gültig ist.

Funktionen, die von Seite und Relais verwendet werden, z. "site-functions.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);
}
?>

Relay Script Verwenden Sie ein vorhandenes Beispiel und fügen Sie vor dem CURL-Block Folgendes hinzu:

// 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

Seiten, die die API benötigen (oder Ihre Javascript-Include-Datei):

<?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);} });

Hinweis: Der Benutzeragent ist spooffähig. IP (REMOTE_ADDR) kann nicht "gefälscht" werden, aber die Einrichtung auf einer Minderheit von Websites kann zu Problemen führen, z. Wenn Sie sich hinter NGINX befinden, enthält REMOTE_ADDR möglicherweise immer die NGINX-Server-IP. 

Wenn Sie eine typische Drittanbieter-API verwenden, die NICHT sensible Informationen bereitstellt, bis Sie die Nutzungsobergrenze für Ihren API-Schlüssel erreicht haben, sollten die oben genannten Lösungen (meiner Meinung nach) ausreichend sein.

4
scytale

Wie die Leute darauf hingewiesen haben, möchten Sie eine Proxy-Methode auf Ihrem Server, um den API-Schlüssel auszublenden.

Um einen Missbrauch Ihrer Methode auf dem Server zu vermeiden, schützen Sie den Anruf mit einem einmaligen Token (wie Sie es normalerweise für Formulare verwenden), das von Ihrem Server generiert wird (nicht in Javascript).

Ich bin kein Fan des codierten eingefügten Codes, der nach bekannten http-user-Agents ... oder Site-Tokens sucht ... dies ist nicht sicher.

2
michael - mlc

Wenn Sie cUrl verwenden, müssen Sie Ihren Server schützen. Ich persönlich benutze das reCaptcha von Google, mit dem Sie Probleme wie Sie lösen können. Die Integration in Client- und Serverseite wurde hier Schritt für Schritt sehr gut erklärt. https://webdesign.tutsplus.com/tutorials/how-to-integrate-no-captcha-recaptcha-in-ihre-website--cms-23024 Auf diese Weise spenden Sie Sie müssen nichts an Ihren Virtualhost-Dateien und Apache-Konfigurationen ändern.

1
Luis Gar

Ich würde die Lösung @MMRahman Published verwenden, wenn Sie eine Sicherheitsschicht zwischen Ihrem Backend und Ihrem Frontend hinzufügen möchten. Was Sie tun könnten, wenn der Benutzer beim Anmelden eine eindeutige ID generiert, in der Serversitzung und in einem Cookie oder speichert lokaler/Sitzungsspeicher des Browsers. Wenn Sie Ihr Backend mit ajax aufrufen, können Sie den Wert von der Stelle abrufen, an der Sie im Browser gespeichert sind, und überprüfen, ob die Werte gleich sind. Wenn ja, rufen Sie die externe API und auf Gibt die Werte zurück, wenn die Anforderung nicht ignoriert wird.

Zusammenfassend also: Benutzeranmeldung -> eindeutige ID generieren -> in Serversitzung und Browsersitzung speichern -> Anruf von ajax aufrufen und als Parameter den Wert von Browsersitzung übergeben - prüfen, ob er mit dem in der Serversitzung gespeicherten Wert -> übereinstimmt Wenn ja, rufen Sie die externe API mit dem Token an, das in Ihrem Backend/db/file/gespeichert ist 

0
Emilio Camacho