Client-Hostname in PHP auslesen

Client-Hostname in PHP auslesenIn folgendem Artikel soll es darum gehen, wie man mittels PHP den Client-Hostname, also den Computernamen des Besuchers, auslesen kann. Sucht man im Netz, wird man schnell feststellen, dass das Ganze gar nicht so trivial ist. Entweder man bekommt schlichtweg falsche Antworten oder man bekommt Lösungen, die nicht in PHP sondern z.B. in VBScript umgesetzt sind oder es wird gesagt, dass es gar nicht geht. Da es jedoch funktioniert, möchte ich heute zeigen wie man den Computernamen auslesen kann.

Doch bevor wir die Lösung anschauen, werfen wir noch einen kurzen Blick auf die vorgeschlagenen Lösungen im Netz und erläutern, warum diese falsch sind bzw. nicht empfehlenswert.

Wie man den Client-Hostname nicht ausliest

Oft vorgeschlagen, aber definitiv falsch ist:

echo gethostname();

Der Befehl liest zwar einen Hostname aus, jedoch ist dies der Name des Servers, auf dem das Script ausgeführt wird und nicht der des Clients. Das ganze funktioniert also nur, wenn das PHP-Skript lokal ausgeführt wird. In diesem Fall kennt man den Client-Hostname aber sowieso.

Auch folgendes habe ich öfters im Netz gelesen:

echo gethostbyaddr($_SERVER['REMOTE_ADDR']);

Dieser Code nimmt sicht zuerst die öffentliche IP-Adresse des Clients ($_SERVER[‘REMOTE_ADDR’]) und versucht dann dazu den Hostnamen zu ermitteln. Üblicherweise verbirgt sich hinter der öffentlichen Addresse aber ein Router und dahinter viele Clientgeräte. Da das Script auf einem Server läuft, dieser aber nicht im Client-Netz steht, hat er auch keinen Zugriff auf den DNS, der die Hostnamen der Clients kennt. Es wird also entweder nur die IP-Adresse zurückgegeben oder höchsten des Hostname, den der Internetprovider der öffentlichen IP zugeordnet hat. Das hilft also auch nicht.

Folgende Lösung wird auch oft vorgeschlagen und funktioniert immerhin in manchen Fällen:

var network = new ActiveXObject('WScript.Network');
alert(network.computerName);

Jedoch hat auch diese Lösungen mehrere Einschränkungen. Zum einen ist die Lösung Javascript und ActiveX basiert. Um den ausgelesenen Wert also wieder auf dem Server auswerten zu können, müsste man diesen noch per Ajax an den Server zurücksenden. Zudem funktioniert diese Lösung nur im Internet Explorer, was den praktischen Nutzen noch einmal verringert.

Nachdem nun also klar ist, warum die verschiedenen Lösungsvorschläge ungeeignet sind, wollen wir uns anschauen, wie man trotzdem an den Clientname kommt.

Computername in PHP auslesen

Und wie geht es nun “richtig”? Eine Lösung, die sowohl im Internet Explorer, Firefox, Edge als auch Chrome funktioniert, macht sich den “Authorization”-Header  zu nutze. Per PHP-Skript wird vom Besucher eine Authorization im “NTLM”-Format angefordert. Daraufhin bekommt der Nutzer einen Login-Dialog angezeigt. Die eingegebenen Nutzerdaten werden dann als NTLM-Hash an den Server übermittelt. Der Clou daran? Der übermittelte Hash enthält den lokalen Computernamen – ergo den von uns gesuchten “Client Hostname”.

In der Praxis besteht die Implementierung aus zwei Teilen. Zum einen aus dem Code, der die NTLM-Authentifizierung anfordert und zum anderen aus dem Code, der den Computernamen aus dem NTLM-Hash ausliest.

if( !function_exists('apache_request_headers') ) {

	function apache_request_headers() {
		$arh = array();
		$rx_http = '/\AHTTP_/';
		foreach($_SERVER as $key => $val) {
			if( preg_match($rx_http, $key) ) {
				$arh_key = preg_replace($rx_http, '', $key);
				$rx_matches = array();

				$rx_matches = explode('_', $arh_key);
				if( count($rx_matches) > 0 and strlen($arh_key) > 2 ) {
					foreach($rx_matches as $ak_key => $ak_val) $rx_matches[$ak_key] = ucfirst($ak_val);
					$arh_key = implode('-', $rx_matches);
				}
				$arh[$arh_key] = $val;
			}
		}
		return( $arh );
	}
}
$headers = apache_request_headers();

if (!isset($headers['AUTHORIZATION']) || substr($headers['AUTHORIZATION'],0,4) !== 'NTLM'){
        header('HTTP/1.1 401 Unauthorized');
        header('WWW-Authenticate: NTLM');
        exit;
}

Oben stehender Code fordert die Authentifizierung per NTLM-Verfahren an. Wichtig ist, dass der Code ganz zu Beginn des PHP-Skripts eingebaut wird, da eine Modifizierung der Header nur möglich ist, solange das Skript noch keine anderen Informationen an den Client übersendet hat.

Das Skript liest mittels der “apache_request_headers”-Funktion die aktuellen Header aus und überprüft, ob ein Authorization-Header-Feld vorhanden ist und mit dem String “NTLM” beginnt. Ist dies nicht der Fall, wird die Authentifizierung angefordert.

Im zweiten Schritt, welcher ausgeführt wird, nachdem sich der Client per NTLM authentifiziert hat, wird nun der Clientname ausgelesen.

$auth = $headers['AUTHORIZATION'];

if (substr($auth,0,5) == 'NTLM ') {
        $msg = base64_decode(substr($auth, 5));
        if (substr($msg, 0, 8) != "NTLMSSP\x00")
                die('error header not recognised');

        if ($msg[8] == "\x01") {
                $msg2 = "NTLMSSP\x00\x02"."\x00\x00\x00\x00". 
                        "\x00\x00\x00\x00". 
                        "\x01\x02\x81\x01". 
                        "\x00\x00\x00\x00\x00\x00\x00\x00". 
                        "\x00\x00\x00\x00\x00\x00\x00\x00". 
                        "\x00\x00\x00\x00\x30\x00\x00\x00"; 

                header('HTTP/1.1 401 Unauthorized');
                header('WWW-Authenticate: NTLM '.trim(base64_encode($msg2)));
                exit;
        }
        else if ($msg[8] == "\x03") {
                function get_msg_str($msg, $start, $unicode = true) {
                        $len = (ord($msg[$start+1]) * 256) + ord($msg[$start]);
                        $off = (ord($msg[$start+5]) * 256) + ord($msg[$start+4]);
                        if ($unicode)
                                return str_replace("\0", '', substr($msg, $off, $len));
                        else
                                return substr($msg, $off, $len);
                }
                $user = get_msg_str($msg, 36);
                $domain = get_msg_str($msg, 28);
                $workstation = get_msg_str($msg, 44);
                print "You are $user from $workstation.$domain";
        }
}

Nach dem das Skript durchgelaufen ist, findet sich in der Variable “$workstation” der Client-Hostname des Skript-Aufrufers, respektive des Website-Besuchers.

Abschließend möchte ich meinen besonderen Dank an Loune aussprechen, auf dessen Arbeitsergebnissen dieser Blogpost entstanden ist.

Raffi

Seit 2011 blogge ich hier über Programmierung, meine Software, schreibe Tutorials und versuche mein Wissen, so gut es geht, mit meinen Lesern zu teilen.

2 Kommentare

  1. Fabiansays:

    Hallo,
    die Lösung wäre schön und genau das was ich suche. Funktioniert aber leider nicht (mehr?). Es poppt immer wieder das Authentifizierungsformular auf…

    • Hallo Fabian,
      das ist (leider) korrekt so. Das Pop-Up kommt immer hoch, du kannst jedoch einfach ok drücken (egal, was als Nutzer/Passwort) eingegeben wurde und das PHP-Script ließt anschließend den PC-Namen aus. In dem Artikel ging es mir darum, zu zeigen, dass es technisch möglich ist. Sauber ist diese Lösung natürlich nicht. Der Trick ist ja, dass per NTLM-Header erzwungen wird, dass der Anmeldedialog hochkommt. Der Browser gibt beim Bestätigen dieses Dialogs automatisch den Rechnernamen preis… Der Dialog muss also bzw. ist Grundlage für diese Methode.

Hinterlasse einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Sie dient nur dem Spamschutz.