Android – reCaptcha Implementierung
In diesem Tutorial zeige ich wie ihr die reCaptcha Implementierung für eure Android App. Mit dem reCaptcha Service von Google schützt man Programme vor Bots. Das ist zunehmend auch für Android Apps relevant, insbesondere, wenn der Benutzer durch häufige Verwendung einen Vorteil jeglicher Art gegenüber anderer App Nutzer gewinnt.
Android – reCaptcha Implementierung
Für das reCaptcha müssen folgende Voraussetzungen erfüllt sein:
- Google Account
das reCaptcha Service wird von Google bereit gestellt. Man muss dort jede App über dessen Paketbezeichnung verifizieren. - Android App mit Server Kommunikation
reCaptcha wird über 2 Wege verifiziert, weshalb man das Service nur mit einer Client App verwenden kann die mit einem Server kommuniziert. Das macht nicht bei allen Apps Sinn.
Durch diese Voraussetzungen wird klar, die reCaptcha Implementierung schützt das auf dem Server laufende Webservice vor Manipulation. Ein kleines Beispiel, dass viele kennen werden:
Bei einem Free-To-Play Spiel kann der Benutzer in seiner App auf einen Button klicken und eine Ressource einsammeln. Diese hat einen Cooldown, man kann diese Aktion also nur alle paar Minuten ausführen. Bei der Aktion schickt die Android App einen Request an den Server, je nach Status wird diese ausgeführt, oder nicht. Ist dem Angreifer diese Kommunikation bekannt (beispielsweise durch den Source Code der Client App), so könnte er diesen Request nachbauen und von seinem Server schicken. Damit könnte der Spieler diese Ressource immer erhalten, auch Nachts wenn er schläft. Das wäre nicht im Sinne des Services.
reCaptcha registrieren
Das reCaptcha Service wird zuerst bei Google für die eigene App beantragt. Dazu folgt man dem Link und wählt reCaptcha-Android als Typ aus. Für Webseiten Formulare stehen noch zwei weitere Typen bereit.
Man muss hier lediglich die Paketnamen der Android Apps eingeben für welche man reCaptcha verwenden will. Danach noch Nutzungsbedingungen registrieren und fertig. Als Ergebnis bekommt nun 2 Schlüssel:
Der Websiteschlüssel muss in der App eingefügt werden (darüber wird das reCaptcha angefordert), der zweite Schlüssel dient zur Verifikation des Ergebnisses vom User (gleich dazu mehr). Weiter geht es mit der Implementierung.
reCaptcha implementieren (Client Validierung)
Zuerst binden wir laut Android Dokumentation das Service ein. Dazu fügt man der App Level build.gradle Datei des Projekts die Zeile mitcom.google.android.gms:play-services-safetynet:11.2.0 hinzu:
apply plugin: 'com.android.application' ... dependencies { compile 'com.google.android.gms:play-services-safetynet:11.2.0' }
Beim Button, der durch das reCaptcha geschützt werden soll müssen wir nun noch den Aufruf des Services einbauen. Das klappt mehr oder weniger gut…
public void onClick(View v) { SafetyNet.getClient(this).verifyWithRecaptcha(YOUR_API_SITE_KEY) .addOnSuccessListener((Executor) this, new OnSuccessListener() { @Override public void onSuccess(SafetyNetApi.RecaptchaTokenResponse response) { // Indicates communication with reCAPTCHA service was // successful. String userResponseToken = response.getTokenResult(); if (!userResponseToken.isEmpty()) { // Validate the user response token using the // reCAPTCHA siteverify API. } } }) .addOnFailureListener((Executor) this, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { if (e instanceof ApiException) { // An error occurred when communicating with the // reCAPTCHA service. Refer to the status code to // handle the error appropriately. ApiException apiException = (ApiException) e; int statusCode = apiException.getStatusCode(); Log.d(TAG, "Error: " + CommonStatusCodes .getStatusCodeString(statusCode)); } else { // A different, unknown type of error occurred. Log.d(TAG, "Error: " + e.getMessage()); } } }); }
Die offensichtliche Änderung ist nun YOUR_API_SITE_KEY mit dem zuvor erstellten Websiteschlüssel zu ersetzen. Die meisten fehlenden Pakete solltet ihr im Android Studio durch ALT+ENTER bekommen. Bei mir war SafetyNet erst nach einem Kompilieren verfügbar. Der Code ist recht einfach zu lesen:
- das reCaptcha wird in der App angezeigt
- je nach Ergebnis wird der OnSuccessListener aktiv oder der onFailureListener
Wurde das Captcha erfolgreich gelöst, dann wird das userResponseToken zurückgegeben. Der weitere Programmcode für die erfolgreiche Verifikation erfolgt nun in der onSuccess Methode. Im Fehlerfall werden aktuell Meldungen im Log ausgegeben. Es macht Sinn darauf zu reagieren.
OnClickListener
Ich verwende die onClick Methode in einem OnClickListener, weshalb man die this Referenz verändern musste. Es reicht ein Ersetzen mit
MyActivity.this
Wobei MyActivity die Activity ist, von der aus das Click Event aufgerufen wird. Damit das funktioniert muss diese Activity die Klasse Executor implementieren. Dazu ist es notwendig die Methode execute auszuprogrammieren. In meinem Fall ist diese recht kurz:
public void execute(Runnable r) { r.run(); }
Mögliche Probleme
Ich hatte bei der Implementierung zwei Probleme die etwas schwerer zu lösen waren:
- App kann nicht kompiliert werden, weil das SafetyNet Package nicht gefunden wird.
Das liegt an einem Android Studio Bug, der durch hinzufügen des Maven Services in der Root Level build.gradle gelöst wird:allprojects { repositories { jcenter() maven { url "https://maven.google.com" } } }
- inkompatible Versionen der dependencies
Falls ihr in diese Situation kommt, dann nicht gleich aufgeben. Es ist relativ kompliziert das Problem zu lösen, ohne jetzt jede verwendete Bibliothek und dessen Versionsnummern zu kennen. Das Problem wird je nach Paketen und Versionen in anderen Konstellationen auftreten, weshalb eine allgemein Lösung hier nicht beschrieben werden kann. In meinem Fall hat es geholfenclasspath 'com.google.gms:google-services:3.1.0'
von 3.0.0 auf 3.1.0 zu heben und
apply plugin: 'com.google.gms.google-services'
ganz ans ende der build.gradle zu stellen. Hin und wieder hilft es auch andere Bibliotheken auf eine höhere Version zu heben beziehungsweise compileSdkVersion, minSdkVersion und targetSdkVersion auf eine aktuellere Version zu setzen.
Server Validierung
Aktuell wurde der User am mobilen Android Gerät über unsere reCaptcha Implementierung validiert. Als Rückgabe haben wir ein userResponseToken bekommen. Dieses schicken wir beim Request mit zum Server. Mit einem einfachen Post können wir nun mit dem userResponseToken alle Details zur Validierung einsehen. Das geht so (meine PHP Implementierung):
function captcha(userResponseToken) { $url = "https://www.google.com/recaptcha/api/siteverify"; $data = "secret=SECRET&response=".userResponseToken; $return = curl($url, $data); $array = json_decode($return, true); if($array['success']) return "ok"; else return "not ok"; } function curl($url, $data) { $process = curl_init($url); curl_setopt($process, CURLOPT_TIMEOUT, 30); curl_setopt($process, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($process, CURLOPT_SSL_VERIFYPEER, TRUE); curl_setopt($process, CURLOPT_USERAGENT, "android"); curl_setopt($process, CURLOPT_POSTFIELDS, $data); curl_setopt($process, CURLINFO_HEADER_OUT, true); $return = curl_exec($process); curl_close($process); return $return; }
Als SECRET setzt ihr einfach den Wert von „Geheimer Schlüssel“ vom registrierten reCaptcha ein. Das Service liefert ein JSON Objekt mit folgenden Informationen zurück:
{ "success": true|false, "challenge_ts": timestamp, // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ) "hostname": string, // the hostname of the site where the reCAPTCHA was solved "error-codes": [...] // optional }
In meinem Fall interessiert mich nur der Wert „success„. Ist dieser true, dann handelt es sich um einen Menschen und der Request kann normal abgearbeitet werden. Falls dort false steht, wird am Server nichts gemacht. Je nach dem kann man am Client eine Fehlermeldung ausgeben.
Mithilfe
Anbei noch zwei Screenshots die das Ganze in der Praxis zeigen. Ihr könnt auch gerne die App herunterladen: Bitcoin Factory. Ich wäre euch überaus Dankbar, wenn ihr meiner App eine 5 Sterne Wertung gebt.
Fazit
Die reCaptcha Implementierung für Android ist im Prinzip recht einfach. Es gibt aber ein paar Stolpersteine die man erst einmal überwinden muss. Ich habe in diesem Artikel alle Informationen zusammengefasst, damit ihr problemlos das Service für eure App nutzen könnt.
Gut beschrieben. Hab das ganze selbst vor ein paar Tagen implementiert 🙂
Nur muss ich leider schlechte Erfahrung mit dem Recaptcha für Android melden. In Deutschland gibt es ja leider die unangenehme Drosselung der Internetverbindung, sobald das Datenvolumen aufgebraucht wurde. Das Recaptcha für Android ist da offensichtlich sehr empfindlich und lädt dann ewig bis es zum TImeout kommt. Man kann das auch sehr gut bei der Entwicklung simulieren, in dem man mal vorübergehend die Datenverbindung auf „nur“ edge oder umts stellt. Während einfache http(s) Anfragen noch funktionieren, scheitert das Recaptcha bei der Überprüfung. Offensichtlich werden dermaßen viele Informationen über Hard- und Software übermittelt, dass die Verbindung zusammenbricht.
Das macht die Sache für die Praxis leider eher unattraktiv. Ich hoffe Google bessert da noch nach und verringert die Datenmenge, die da wohl an den Recaptcha Server übermittelt wird (oder komprimiert sie eventuell anders).
ah danke. Das erklärt, warum ich von 2 Usern bereits eine Rückmeldung dazu bekommen hab. Die kommen da offenbar nicht drüber. Ja hoffentlich bessert da Google nach.
Sorry, aber wenn ich erst einmal Bilderrätsel lösen muss, irgendwelche verrauschten Worte erhören oder was Google sonst noch so ständig Neues einfällt, dann werde ich den entsprechenden Dienst mit 99-prozentiger Sicherheit nicht nutzen.
Google reCaptcha ist m.E. der größte Mist seit Erfindung des Internet. Absicherung muss heutzutage leider sein, aber das Internet sollte trotzdem benutzbar bleiben.
Kill reCaptcha
Bin genau deiner Meinung. reCaptcha ist echt nervig…aber es ist auch die einzige vernünftige Möglichkeit Formulare vor Bots zu schützen. Glaub mir, wenn dein Webshop mal von hunderten Anfragen geflutet wir bist du über so einen Mechanismus mehr als dankbar. Es gibt leider einfach keine Alternative die nicht manipulierbar wäre.