“Wenn etwas nicht einfach zu verstehen ist,
muss es eben anders erklärt werden ...”

Durchlasskontrolle mit Googles reCAPTCHA

Einsatz von Captchas bereits auf dem ReverseProxy

Bei Kunden sehe ich immer wieder interne Webangebote, die im Internet veröffentlicht werden. Häufige Beispiele für solche Webseiten sind Web-basierte Email-Zugänge, wie z.B. Outlook Web Access (owa), oder die Veröffentlichung von Intranet-Anwendungen wie Sharepoint-Seiten oder Wiki-Systemen.

Damit die dahinterliegenden Informationen nur von den richtigen Personen eingesehen werden können, erfolgt eine Authentifizierung der Benutzer beim Aufruf der betreffenden Seite.

Das Problem: die öffentlich zugängliche Benutzerauthentifizierung

Wo liegt das Problem mit derart veröffentlichten Web-Inhalten? Das Problem ist die öffentlich zugängliche Benutzerauthentifizierung. Ein potentieller Angreifer könnte hier beispielsweise von außen durch Ausprobieren (Brute-Force) an gültige Benutzerkennungen gelangen. Dem kann der Administrator dadurch entgegentreten, indem er Benutzerkennungen nach einer festgelegten Anzahl falscher Anmeldeversuche sperren lässt. Wozu das wohl führt, wenn ein automatisiertes Skript trotzdem versucht, die Website mit allen möglichen Benutzer/Passwort-Kombinationen anzusprechen?

Mögliche Lösung: Captchas

Viele Webseiten setzen als Lösung für dieses Problem auf den Einsatz von sogenannten Captchas. Website-Benutzer müssen kryptische Zeichenketten entziffern oder Rechenaufgaben lösen, und zu demonstrieren, dass es sich um eine echte Person handelt, die da gerade die Anmeldemaske ausfüllt.



reCAPTCHA alte Version
Eine gute und gern verwendete Captcha API ist reCAPTCHA. Während diese Captcha-Lösung von Google den Benutzer in den letzten Jahren mit immer komplizierteren Zeichenketten forderte, ist die Ende 2014 vorgestellte API Version 2.0 für den Anwender beeindruckend einfach zu bedienen.

reCAPTCHA API V 2.0
Wer Googles Captcha-Lösung einsetzen will, muss sich zuerst einen API-Key von der reCAPTCHA Website besorgen. Anschließend wird im abzusendenden Formular mit vorbereitetem Javascript und einem div-Container das Captcha auf der eigenen Webseite eingebaut. Die Dokumentation dazu sowie Beispiele für die Implementierung und Integration in bestehende Anwendungen sind im "Developer's Guide" und an vielen anderen Stellen im Netz zu finden.

Implementierung auf dem ReverseProxy

Was aber, wenn in der veröffentlichten Anwendung der Einsatz von Captchas nicht vorgesehen oder nur schwer umzusetzen ist? Dann könnte der ReverseProxy hilfreich sein, der bei vielen Netzwerken den externen Zugangspunkt für die intern gehosteten Systeme bereitstellt.


In der Beispiel-Implementierung, die so in ähnlicher Form bei Kunden im Einsatz ist, geht es um den Schutz einer Outlook Web Access (OWA) - Bereitstellung. Auf dem ReverseProxy in der DMZ wird Apache als Proxy- und Filtersoftware eingesetzt.

Das Konzept im Groben

Lösen des Captchas

Auf dem Apache-ReverseProxy wird eine Default-Webseite gehostet, die im einfachsten Fall ausschließlich die Logik zum Lösen des reCaptchas implementiert. Bei erfolgreich gelöstem Captcha setzt die Website einen Cookie, um den Benutzer im Erfolgsfall bei den nächsten Requests wiederzuerkennen.

Damit der Cookie nicht einfach gefälscht werden kann, wird der zufällig generierte Inhalt des Cookies zum späteren Vergleich in einer temporären Datei im Dateisystem des Webservers zwischengespeichert. 

Diese Vorgehensweise kann gleichzeitig auch dafür verwendet werden, um den Benutzer nach einer gewissen Zeit (z.B. nach einer Woche) wieder mit einem Captcha zu konfrontieren. Dazu muss dann einfach z.B. per Cron-Job das temporäre Cookie-File gelöscht werden.

Freischalten des Benutzers

Das Freischalten des Benutzers erfolgt in zwei Schritten in der Logik der Apache-Konfiguration. Zuerst muss geschaut werden, ob im Browser-Request ein passendes Cookie vorhanden ist. Als nächstes wird verglichen, ob der Inhalt des Cookies mit einem der gespeicherten Werte übereinstimmt. Ist das der Fall, werden die ReverseProxy Regeln für die Weiterleitung der Requests an den Backend Server aktiviert.

Requests, die ohne Cookie oder mit falschem Cookie-Inhalt beim Apache eintreffen, werden auf die konfigurierte Default-Website weitergeleitet, die den Benutzer mit einem Captcha konfrontiert.

Implementierung am Beispiel

Die Website mit dem Captcha

Die hier Aufgeführte PHP-Seite demonstriert das Anzeigen des Formulars mit zu lösendem Captcha und die Logik, um bei korrekt gelöstem Captcha einen Sitzungsschlüssel zu generieren, im Dateisystem zu speichern und das Cookie "re_captcha" zu setzen.

/srv/www/htdocs/index.php:

 <?php  
// Initialisierung der API
require_once('./recaptchalib.php');
$publickey = "xxxxxxxxxPublicKeyxxxxxxx";
$privatekey = "xxxxxxxxPricateKEyxxxxxxx";
$captcha_solved=0;$session_dir="/opt/reCaptcha/sessions/";
// Lösungsversuch?
if (array_key_exists("recaptcha_response_field",$_POST)) {
$resp = recaptcha_check_answer ($privatekey,
$_SERVER["REMOTE_ADDR"],
$_POST["recaptcha_challenge_field"],
$_POST["recaptcha_response_field"]);
// Captcha gelöst?
if ($resp->is_valid) {
$captcha_solved=1;
$SESSION_FILE="";
// Erzeugen einen "zufälligen" Keys und Ablage als Session-File
while (! $SESSION_FILE or file_exists($SESSION_FILE)) {
$SSTRING=$_SERVER['REQUEST_TIME'].rand()
.getmypid().$_SERVER['REMOTE_ADDR']
.$_SERVER['HTTP_USER_AGENT'];
$SID=sha1($SSTRING);
$SESSION_FILE="$session_dir/$SID"; }
touch($SESSION_FILE);
// setzen des Cookies mit dem Session-Key und einer Woche Gültigkeit
setcookie('re_captcha',$SID,time()+(3600*24*7));
}
}
?>
<html>
<head>
<?php
if ($captcha_solved == 1) {
echo "<meta http-equiv=\"refresh\" content=\"0\" /> ";}
?>
<script type="text/javascript">
var RecaptchaOptions = {theme : 'white',lang : 'de'};
</script>
</head>
<body>
.... Schönes HTML für schöne Website ;) ....
<form method="post">
<?php
// Wenn das Captcha nicht gelöst wurde, dann Captcha anzeigen
if ($captcha_solved == 0) {
echo recaptcha_get_html($publickey);
echo '<input style="float:right;" type="submit" value="Ok">';
}
?>
</form>
.... Schönes HTML für schöne Website ;) ....
</body>
</html>

Die Apache-Konfiguration mit Cookie-Logik

In der Apache-Konfiguration findet die Logik statt, die "echte" Benutzer anhand des gesetzten Cookies erkennt und den Inhalt mit den gespeicherten Sitzungsschlüsseln abgleicht.

Die ReverseProxy-Funktionalität wird mit einer "RewriteRule" implementiert, die Cookie-Logik mit den vorgeschalteten  "RewriteCond"-Statements. Das zweite "RewriteCond"-Statement bezieht sich auf eine RewriteMap, die per Skript realisiert wurde (siehe unten).  Die passende Dokumentation gibt es unter http://httpd.apache.org/docs/current/mod/mod_rewrite.html

/etc/apache2/sites-enabled/000-default:

 # ... allgemeine Konfiguration ausgelassen  
RewriteMap CaptchaSolved prg:/opt/reCaptcha/check_ReCaptcha.py
RewriteEngine on
SSLProxyEngine on
# Erst die 2 Bedingungen, dann die RewriteRegel für den ReverseProxy-Modus
RewriteCond %{HTTP_COOKIE} re_captcha=([^;]+)
RewriteCond ${CaptchaSolved:%1} 1
RewriteRule ^/owa(.*) https://interner.exchange.server/owa$1 [P,QSA]
# Rewrite von unbekannten URLs
RewriteRule ^/owa/.+ /owa/ [nocase,R=302]
# ... weitere Konfiguration ausgelassen ...



RewriteMap als Cookie-Tester mit Skript

Die zweite Rewrite-Condition bezieht sich auf eine RewriteMap, die als Skript realisiert ist. Dieses Skript liest fortlaufend übergebene Inhalte der "re_captcha"-Cookies von der Standardeingabe. Ist ein passendes Session-File vorhanden, gibt das Skript auf der Standardausgabe eine "1" aus, ansonsten eine "0". Die Dokumentation zu einer RewriteMap mit externem Skript findet Ihr hier: http://httpd.apache.org/docs/current/rewrite/rewritemap.html#prg

/opt/reCaptcha/check_reCaptcha.py:

 #!/usr/bin/python -u  
import os.path
import sys
session_path="/opt/reCaptcha/sessions"
while 1:
session_id = sys.stdin.readline().strip()
if os.path.exists("%s/%s" % (session_path,session_id)):
print 1
else:
print 0


Fazit

Mit dem dargestellten Ansatz implementiere ich immer wieder auf Systemen die Trennung von "echten" Benutzer von den Bots und Skripten. Eine Anpassung an die Nutzung einer anderen ReverseProxy-Software (squid, nginx) solltet Ihr damit auch realisieren können. An seine Grenzen gelangt die vorgestellte Logik dann, wenn URLs geschützt werden sollen, die nicht von Menschen, sondern von Geräten verwendet werden (z.B. ActiveSync).
Du hast Fragen zum Thema? Fordere mich heraus! Frag Robert!