Article not available in English!
Sorry, the content is only available in the German language.
Sorry, the content is only available in the German language.
Vor Weihnachten ist Amazon Echo / Alexa bei mir eingezogen, wahrscheinlich eher aus Neugierde was man damit alles noch anstellen kann. Die Tatsache das Amazon eine API für das Echo System bietet war letztendlich der Ausschlaggebende Grund zum Kauf.
Vieles ist hierzulande leider noch der US Version hinterher, gerade was die Verfügbarkeit der sogenannten Skills angeht. Dies sind mehr oder minder „Apps“ – eigentlich Schnittstellen Verbindungen mit anderen Diensten – die Alexa um mehr oder weniger sinnvolle Funktionen erweitern. Leider ist nach dem ersten Blick in den Skill Katalog deutliche Ernüchterung eingetreten. Viele der hier verfügbaren Skills sind unterm Strich einfach nur schlecht und bieten meist keinen wirklichen Mehrwert.
Nun habe ich mich auch mal an einen eigenen Skill gewagt, allerdings kein öffentlicher für den Skill Katalog, sondern etwas auf meinen Haushalt Zugeschnittenes. Dazu muss man wissen das ich mir vor einigen Jahren einen Raspberry Pi basierten CatFeeder gebaut habe. Diesen mit Alexa zu vernetzen war nun das Ziel, klappte erstaunlich einfach.
Der Skill kann aktuell nur die beiden Aktionen: Füttern und Zeit der letzten Fütterung wiedergeben. Reicht auch erst mal für einen Futterautomaten.
Technisch
Der offizielle Amazon weg sieht dabei eine Verwendung von sogenannten AWS Lambda Services vor, ich habe mich hier mal für eine andere Herangehensweise entschieden. Wenn man sowieso eigene DEV VPS im Netz laufen hat spricht auch nichts dagegen statt Lambda einen selbst gehosteten „Skill Adapter“ zu programmieren. In meinem Fall einfach auf Basis von Nginx + PHP.Daher ist der Weg ab (4) in der Grafik bei mir etwas anders, meine dev Box im Internet wartet auf eingehende „Calls“ vom Alexa Service und stellt dann bei Bedarf eine Verbindung zu meinem Heimnetz / CatFeeder über SSL her. Damit bleibt die Verbindung durch mich kontrolliert und wird zusätzlich zur SSL Verbindung noch in meiner Firewall zuhause auf die Feste IP meiner DEV Box begrenzt. Quasi meine eigene „Device Cloud“.
Im Netz gibt es bereits etliche Scripte auf PHP Basis welche einen Alexa Service abbilden. Leider war alles gefundene teils mit schwerwiegenden Problemen was die Sicherheit angeht behaftet. Amazon gibt selbst bereits einige Regeln vor die man beachten sollte. So werden alle Anfragen z.B. per SignatureCertChainUrl in Form einer SHA1 Checksumme des RAW Request signiert. Viele vorhandene PHP Ansätze konzentrieren sich jedoch lediglich auf die Skill Aktion und sichern eingehende Verbindungen überhaupt nicht ab.
Um dem Abhilfe zu schaffen und als kleine Vorlage packe ich mein Code mal auf GIST. Hier ein Auszug aus den Cert Checks:
if (preg_match("/https:\/\/s3.amazonaws.com(\:443)?\/echo.api\/*/i", $_SERVER['HTTP_SIGNATURECERTCHAINURL']) == false) {
ThrowRequestError(403, "Forbidden, unkown SSL Chain Origin!");
}
// PEM Certificate signing Check
// First we try to cache the pem file locally
$local_pem_hash_file = sys_get_temp_dir() . '/' . hash("sha256", $_SERVER['HTTP_SIGNATURECERTCHAINURL']) . ".pem";
if (!file_exists($local_pem_hash_file)) {
file_put_contents($local_pem_hash_file, file_get_contents($_SERVER['HTTP_SIGNATURECERTCHAINURL']));
}
$local_pem = file_get_contents($local_pem_hash_file);
if (openssl_verify($rawJSON, base64_decode($_SERVER['HTTP_SIGNATURE']) , $local_pem) !== 1) {
ThrowRequestError(403, "Forbidden, failed to verify SSL Signature!");
}
// Parse the Certificate for additional Checks
$cert = openssl_x509_parse($local_pem);
if (empty($cert)) ThrowRequestError(424, "Certificate parsing failed!");
// SANs Check
if (stristr($cert['extensions']['subjectAltName'], 'echo-api.amazon.com') != true) ThrowRequestError(403, "Forbidden! Certificate SANs Check failed!");
// Check Certificate Valid Time
if ($cert['validTo_time_t'] < time()) {
ThrowRequestError(403, "Forbidden! Certificate no longer Valid!");
// Deleting locally cached file to fetch a new at next req
if (file_exists($local_pem_hash_file)) unlink($local_pem_hash_file);
}
Das gesamte PHP Beispiel findet man auf Github zur freien Verwendung.