Philipp Guttmann, LL. B.

Tutorial: Gültiger PHP-Endpoint für Alexa Skill auf eigener Website

Click here for the English version
Dieses Tutorial erklärt, wie man einen gültigen PHP-Endpoint für ein Alexa Skill erstellt, welches alle Anforderungen an die Sicherheit von Amazon erfüllt. Zudem wird auch ein einfacher Rückgabe-Code in JSON gezeigt.

In diesem Tutorial zeige ich Ihnen Schritt für Schritt, wie Sie einen PHP-Endpoint auf Ihrer eigenen Website erstellen, der eine Antwort im JSON-Format zurückgibt und alle Anforderungen an die Sicherheit von Amazons Alexa Skill erfüllt. Am Ende des Artikels gibt es den kompletten Code.

Vorbereitung

Ihre Datei für den PHP-Endpoint sollte damit starten, die JSON-Eingabe von Amazon und die HTTP_SIGNATURECERTCHAINURL zu erhalten, die man über die Variable $_SERVER erhält. Zudem müssen wir die standardmäßige Zeitzone einstellen:

$input = file_get_contents('php://input'); $post = json_decode($input); date_default_timezone_set('UTC'); $SignatureCertChainUrl = $_SERVER['HTTP_SIGNATURECERTCHAINURL'];

Erster Sicherheitscheck

Im ersten Sicherheitscheck (erste Abfrage) müssen Sie die applicationId, den abgefragten Timestamp und die HTTP_SIGNATURECERTCHAINURL wie folgt überprüfen:

Check der applicationId

'amzn1.ask.skill.Ihre Skill-ID' == $post->session->application->applicationId

Fügen Sie die applicationId Ihres Skills im rot markierten Bereich ein. Sie können diese ID unter dem Namen Ihres Skills im Überblick über Ihre Amazon Alexa Skills im Amazon Developer-Bereich finden.

Check des abgefragten Timestamp

$post->request->timestamp > date('Y-m-d\TH:i:s\Z', time()-150)

Check der HTTP_SIGNATURECERTCHAINURL

preg_match('/https:\/\/s3\.amazonaws\.com(:433)?\/echo\.api\//', $SignatureCertChainUrl)

Zweiter Sicherheitscheck

Im zweiten Sicherheitscheck (innerhalb der ersten Abfrage) erstellen wir eine Datei aus dem Inhalt der SignatureCertChainUrl, die wir zuvor in der Vorbereitung als Variable definiert hatten. Dann vergleichen wir deren Inhalt mit dem Inhalt der HTTP_SIGNATURE, um die Signatur mit dem Public Key des Inhalts von SignatureCertChainUrl zu überprüfen. Außerdem müssen wir die URL im entschlüsselten Inhalt der SignatureCertChainUrl und deren Gültigkeitszeit überprüfen.

Überprüfen der Signatur mit Public Key und openssl_verify-Funktion

$SignatureCertChainUrl_File = md5($SignatureCertChainUrl); $SignatureCertChainUrl_File = $SignatureCertChainUrl_File . '.pem'; if (!file_exists($SignatureCertChainUrl_File)) { file_put_contents($SignatureCertChainUrl_File, file_get_contents($SignatureCertChainUrl)); } $SignatureCertChainUrl_Content = file_get_contents($SignatureCertChainUrl_File); $Signature_Content = $_SERVER['HTTP_SIGNATURE']; $SignatureCertChainUrl_Content_Array = openssl_x509_parse($SignatureCertChainUrl_Content); $Signature_PublicKey = openssl_pkey_get_public($SignatureCertChainUrl_Content); $Signature_PublicKey_Data = openssl_pkey_get_details($Signature_PublicKey); $Signature_Content_Decoded = base64_decode($Signature_Content); $Signature_Verify = openssl_verify($input, $Signature_Content_Decoded, $Signature_PublicKey_Data['key'], 'sha1');

Check der URL im entschlüsselten Inhalt der SignatureCertChainUrl

preg_match('/echo-api\.amazon\.com/', base64_decode($SignatureCertChainUrl_Content))

Check der Gültigkeitszeit der Signatur von SignatureCertChainUrl

$SignatureCertChainUrl_Content_Array['validTo_time_t'] > time() AND $SignatureCertChainUrl_Content_Array['validFrom_time_t'] < time()

Check der Gültigkeit der Signatur

$Signature_Content AND $Signature_Verify == 1

Wenn die Überprüfung der Sicherheit fehlschlägt

Wenn eine der Voraussetzungen des Sicherheitschecks fehlschlägt, geben wir den HTTP-Code 400 zurück:

http_response_code(400);

Einen einfachen Satz übergeben

Wenn der Sicherheitscheck erfolgreich ist, können wir eine Ausgabe im JSON-Format zurückgeben:

header ('Content-Type: application/json');

Für den PlainText definieren wir unser PHP-Array wie folgt:

$PHP_Output = array('version' => '1.0', 'response' => array('outputSpeech' => array('type' => 'PlainText')));

Dann füllen wir die Text-Ausgabe mit Inhalt:

$PHP_Output['response']['outputSpeech']['text'] = 'Hallo Welt!';

Zum Schluss wandeln wir unser PHP-Array in ein JSON-Format um und geben es zurück:

echo json_encode($PHP_Output, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

Kompletter Code für einen PHP-Endpoint eines Amazon Alexa Skill

<?php $input = file_get_contents('php://input'); $post = json_decode($input); date_default_timezone_set('UTC'); $SignatureCertChainUrl = $_SERVER['HTTP_SIGNATURECERTCHAINURL']; if ('amzn1.ask.skill.Ihre Skill-ID' == $post->session->application->applicationId AND $post->request->timestamp > date('Y-m-d\TH:i:s\Z', time()-150) AND preg_match('/https:\/\/s3\.amazonaws\.com(:433)?\/echo\.api\//', $SignatureCertChainUrl)) { $SignatureCertChainUrl_File = md5($SignatureCertChainUrl); $SignatureCertChainUrl_File = $SignatureCertChainUrl_File . '.pem'; if (!file_exists($SignatureCertChainUrl_File)) { file_put_contents($SignatureCertChainUrl_File, file_get_contents($SignatureCertChainUrl)); } $SignatureCertChainUrl_Content = file_get_contents($SignatureCertChainUrl_File); $Signature_Content = $_SERVER['HTTP_SIGNATURE']; $SignatureCertChainUrl_Content_Array = openssl_x509_parse($SignatureCertChainUrl_Content); $Signature_PublicKey = openssl_pkey_get_public($SignatureCertChainUrl_Content); $Signature_PublicKey_Data = openssl_pkey_get_details($Signature_PublicKey); $Signature_Content_Decoded = base64_decode($Signature_Content); $Signature_Verify = openssl_verify($input, $Signature_Content_Decoded, $Signature_PublicKey_Data['key'], 'sha1'); if (preg_match('/echo-api\.amazon\.com/', base64_decode($SignatureCertChainUrl_Content)) AND $SignatureCertChainUrl_Content_Array['validTo_time_t'] > time() AND $SignatureCertChainUrl_Content_Array['validFrom_time_t'] < time() AND $Signature_Content AND $Signature_Verify == 1) { header ('Content-Type: application/json'); $PHP_Output = array('version' => '1.0', 'response' => array('outputSpeech' => array('type' => 'PlainText'))); $PHP_Output['response']['outputSpeech']['text'] = 'Hallo Welt!'; echo json_encode($PHP_Output, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } else { http_response_code(400); } } else { http_response_code(400); } die(); ?>

Fragen?

Wenn Sie weitere Fragen haben, können Sie mir gerne eine E-Mail an ed.nnamttug-ppilihp@liam schreiben und ich versuche zu helfen :)