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");
}
?>

Weiter zu Entfernen der Sicherungseinträge beim Logout