Prüfscript
Es handelt sich um eine Implementierung auf Basis von PHP und memcached. Das Skript wird unter GPLv3 veröffentlicht. Bitte beachten Sie die Voraussetzungen.
- shibchecker.php
<?php /* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Copyright 2015 Frank Schreiterer, University of Bamberg, Computing Centre */ #unbegrenzt ausführen set_time_limit(0); #set logging to true or false $logging=true; #Pfad zur Logfile $logfile="/var/log/apache2/map.log"; #apt-get install php5-memchache NICHT php5-memchached! $mcsrv="127.0.0.1"; $mcport="11211"; #Lebenzeit der Einträge in Sekunden -> Wert muss an die SessionLifeTime der Shibboleth-Session und der Anwendungs-Session angepasst sein #hier 12 Stunden $mcExpire=43200; #Das Prüfmuster für die Session-ID der Anwendung $regexappsessionid="/^[a-z0-9]{26}$/"; ### Ende User-Konfig ### #es wird versucht, den Overhead für die Verbingung durch die ständige Verbindung zu minimieren, #schlägt bei memcached die Verbindung fehl, so wird die Verbindung selbständig wieder aufgebaut $mc=new Memcache; $mc->connect($mcsrv,$mcport); $keyboard = fopen("php://stdin","r"); $out = fopen("php://stdout","w"); while (1) { #Variablen initialisieren $shibvalid=false; $appvalid=false ; $valid=true; $return=""; $returnmsg=""; $logstr = ""; $mixedlazy = false; $checkappsessionid = false; $appsessionid=""; $shibcookiesessionid=""; $appsessionidmatch=0; $shibcookiesessionidmatch=0; #Ende Variablen initialisieren $input = trim(fgets($keyboard)); $startts = microtime(true); #input zerlegen $tmp = explode(",",$input); #Context -> Parameter 1 bei Aufruf RewriteMap sessionHook wenn über den LoginHook des Shibd aufgerufen wird, lazy für ausschließlich Lazy Session und normal für normale Shib-Session #Der Parameter 5 ist optional und wird verwendet, um dem shibcheker mitzuteilen, dass es sich um die Prüfung im Modus mixedLazy handelt, d. h. die Anwendung unterstützt neben #Shibboleth Lazy Session und ander Authentifizungen. $context = strtolower(trim($tmp[0])); #Shib-Session-ID -> Parameter 2 bei Aufruf RewriteMap $shibsessionid = trim($tmp[1]); #Name der Anwendungssession -> Parameter 3 bei Aufruf RewriteMap $sesscookie = trim($tmp[2]); #die gesetzen Cookies, aus denen der Wert der Anwendungssession extrahiert werden muss -> Parameter 4 bei Aufruf ReweriteMap $cookies = $tmp[3]; #Parameter 5 mixedLazy für Modus mixedLazy, falls die shibsessionid leer ist if (isset($tmp[4]) && strtolower(trim($tmp[4])) == "mixedlazy") { #Prüfung der übermittelten appsessionid auf Duplikate, wenn im Parameter 5 mixedLazy übergeben wurde $checkappsessionid = true; if ($shibsessionid == "") { $mixedlazy = true; } } #aus den Cookies die ID der Anwendungssession extrahieren $regexapp="/^$sesscookie=.*$/"; $regexshib="/^_shibsession_[a-z0-9]+=_[a-z0-9]{32}/"; $tmp = explode(";",$cookies); foreach ($tmp as $cookie) { $cookie = trim($cookie); if (preg_match($regexapp,$cookie)) { $appsessionid = trim(str_replace($sesscookie."=","",$cookie)); $appsessionidmatch++; } if (preg_match($regexshib,$cookie)) { $shibcookiesessionid = trim(preg_replace("/_shibsession_[a-z0-9]+=/","",$cookie)); $shibcookiesessionidmatch++; } } #Es wurde am Shib-Session-Cookie nichts verändert, Veränderung am Cookienamen werden von Shibd mit einer leeren Session-ID im Header quitiert, jedoch #bleibt das Cookie ansich mit der Session-ID bestehen #wird mehr als einmal versucht, ein Cookie mit der Anwendungssession bzw. Shibbolethsession zu übergeben, ist das auch verboten if ($shibsessionid == $shibcookiesessionid && $appsessionidmatch < 2 && $shibcookiesessionidmatch < 2) { #wenn mixedLazy, dann können wir uns alle weiteren Prüfungen sparen, da die Shibboleth-Checks nur ausgeführt werden können, #wenn auch die Anmeldung über Shibboleth erfolgt. #Anderenfalls liegt Die Prüfung der übergebenen Anwendungssession in der Verantwortung der Anwendung und kann hier nicht geprüft werden. if ($mixedlazy == false) { #grundlegende Prüfungen #shibsessionid if (preg_match("/^_[a-z0-9]{32}$/",$shibsessionid)) { $shibvalid=true; } elseif ($shibsessionid != "") { #eine ungültige ID der Shibboleth-Session wurde übermittelt $logstr = "$logstr\nShibboleth-Session-Id $shibsessionid ungültig"; $valid = false; } #appsessionid - hier ggf. Anpassungen vornehmen (ist für PHP-Session) if (preg_match($regexappsessionid,$appsessionid)) { $appvalid=true; } elseif ($appsessionid != "") { #eine ungültige ID der Anwendungs-Session wurde übermittelt $valid = false; $logstr = "$logstr\nAnwendungs-Session-Id $appsessionid ungültig"; } #ist die Anwendungs-Session gesetzt und noch keine Shib-Session gesetzt ist das böse if ($shibvalid == false && $appvalid == true) { $valid = false; $logstr = "$logstr\nAnwendungs-Session-Id $appsessionid vor Shibboleth-Session gesetzt"; } #wurde über den sessionHook aufgerufen, dann darf im Anwendungscookie nichts drin stehen if ($context == "sessionhook" && $appsessionid != "") { $valid = false; $logstr = "$logstr\nAnwendungs-Session-Id $appsessionid im Modus sessionHook gesetzt - verboten"; } #erweiterte Prüfung nur notwendig, wenn nicht abgemeldet werden soll und nicht der sessonHook geprüft wird if ($valid == true && $context != "sessionhook") { #nur weiter prüfen, wenn die Anwendungs-Session und die Shib-Session gültig sind if ($shibvalid == true && $appvalid == true) { #schauen, ob es die Kombination der Sessions schon gibt $item = $mc->get($shibsessionid); #bei memcached-Cluster hier noch aus den anderen lesen und ggf. Werte lokal schreiben if ($item == false) { $logstr = "$logstr\nneue Shibboleth-Session $shibsessionid"; if ($checkappsessionid == true) { #noch prüfen, ob die Anwendungssession nicht schon einmal vergeben wurde, so kann bei mixedLazy die Gefahr minimiert werden, dass versucht wird, mit einer alten Anwendungssession zuzugreifen $get = $mc->get($appsessionid); if ($get != false) { $valid = false; $logstr = "$logstr\nAnwendungs-Session-Id $appsessionid bereits vergeben - ungültig"; } else { $logstr = "$logstr\nPrüfung auf doppelte Anwendungs-Session-Id $appsessionid erfolgreich - Anwendungs-Session-Id $appsessionid nicht vorhanden"; } } #nur weiter, wenn noch gültig if ($valid == true) { #Eintrag in die Sicherungstabelle zur Session schreiben, wenn auch eine Anwendungssession da ist if ($appsessionid != "") { $mc->set($shibsessionid,$appsessionid,false,$mcExpire); $logstr = "$logstr\nSchreibe Key Shibboleth-Session $shibsessionid mit Wert der Anwendungs-Session $appsessionid"; #bei memcached-Cluster hier noch in die weiteren schreiben if ($checkappsessionid == true ) { #und noch die appsessionid als key $mc->set($appsessionid,"$shibsessionid",false,$mcExpire); $logstr = "$logstr\nSchreibe Sicherungseintrag zur Anwendungs-Session $appsessionid"; } } } } elseif ($valid == true) { #prüfen, ob versucht wurde, zur Shibboleth-Session eine andere Kombination mit der Anwendungs-Session unterzuschieben if ($item != $appsessionid) { $valid = false; $logstr = "$logstr\nAnwendungs-Session $appsessionid zur Shibboleth-Session $shibsessionid übermittelt, aber Wert der Anwendungs-Session $item erwartet - ungültig"; } } } #Anwendungs-Session und die Shib-Session gültig else { $valid = false; #SONDERFÄLLE #erster Aufruf bei Lazy-Session if ($shibsessionid == "" && $appsessionid == "" && $context == "lazy") { $returnmsg = "doLogin"; $valid = true; } #dieser Zustand darf bei normaler und bei lazy Session erreicht werden, bei sessionHook kommen wir gar nicht hier hin #es muss zwingend die Anwendungssession initialisiert werden, sonst ist es möglich, eine falsche Session zu verwenden if ($appsessionid == "" && $shibvalid == true) { $valid = true; $returnmsg = "doAppSession"; $logstr = "$logstr\nShibboleth-Session $shibsessionid ohne Anwendungs-Session gefunden - gültig, aber Initialisierung der Anwendungs-Session erforderlich"; } } } #erweiterte Prüfung } # nicht mixedLazy else { $valid = true; $logstr = "$logstr\nModus mixedLazy ohne Shibboleth-Authentifizierung - Anfrage zugelassen"; } } #$shibsessionid == $shibcookiesessionid else { $shibvalid = false; $valid = false; if ($shibsessionid != $shibcookiesessionid) { $logstr = "$logstr\nShibboleth-Session-Id vom Shibd ungleich Shibboleth-Session-Id aus Cookie"; } else { $logstr = "$logstr\nShibboleth- oder Anwendungs-Cookie mehrfach gesetzt"; } } #die Rückgabewerte if ($valid == false) { $return = "doLogout"; } else { $return = "good"; if ($returnmsg != "") { $return = $returnmsg; } } if ($logging == true) { $log = fopen($logfile,"a+"); $now = date("Y-m-d H:i:s"); $nowts = microtime(true); $extime = ($nowts - $startts)*1000; $logstr="\n$now Ausführungsdauer: $extime Millisekunden \nKontext: $context mixedLazy: $mixedlazy shibvalid: $shibvalid appvalid: $appvalid \nEingabe: $input \nshibsessionid: $shibsessionid \nappsessionid: $appsessionid $logstr \nreturn: $return\n"; fwrite($log,$logstr); fclose($log); } fwrite($out,"$return\n"); } ?>