How to read client hostname in PHP

How to read clienthostname in PHPThe following article is about how to use PHP to read out the client hostname, which is the computer name of a visitor. If you search in the internet, you will find out soon, that the whole thing is not so trivial. Either one gets simply wrong answers or one gets solutions, not in PHP but for example implemented in VBScript or it is said that it is not possible at all. But since it works, I would like to show you how to read the computer name, now.

But before we look at the solution, let’s take a quick look at the suggested solutions on the internet and explain why they are wrong or not recommended.

How to not read the client hostname in PHP

Often suggested but definitely wrong:

echo gethostname();

The command reads a hostname, but it is the name of the server on which the script is running, not the client. The whole thing only works if the PHP script runs locally. But in this case you know the client hostname anyway. So it’s useless.

I have also often read the following in the internet:

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

This code first looks at the public IP address of the client ($_SERVER [‘REMOTE_ADDR’]), and then tries to find the host name. Usually behind the public address you will find a router which hides the “real” clients behind it. Because the script runs on a server, but it is not on the client network, it has no access to the client’s network DNS, which knows the hostnames of the clients. This means that either the IP address is returned, or the hostname which was assigned to the public IP by the internet provider. So that does not help either.

The following solution is often suggested and works in some cases:

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

However, these solutions also have several limitations. For one, the solution is Javascript and ActiveX based. In order to be able to evaluate the read-out value again on the server, you would have to send it back to the server via Ajax. In addition, this solution works only in Internet Explorer, which reduces the practical benefits.

So now it is clear why the different solutions are unsuitable, so let us look at how to get the clientname in PHP for real.

Read out the client’s computername in PHP

And how is this “right” way? A solution, which works in Internet Explorer, Firefox, Edge as well as Chrome, makes use of the “Authorization” header. The PHP script forces the visitor to authorize himself via “NTLM” authorization format. The user is then shown a login dialog. The entered user data is then sent to the server as a NTLM hash. The highlight? The transmitted hash contains the local computer name – ergo the “client hostname” we are looking for. (And it does contain the hostname, regardless of the users input. Even if the user keeps the fields empty, the hash will include the client’s hostname.)

In practice, the implementation consists of two parts. On the one hand, the code that requests NTLM authentication, and on the other hand the code that reads the computer name from the NTLM hash.

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;
}

The above code requests the NTLM authentication. It is important that the code is placed at the very beginning of the PHP script, since it is only possible to modify the headers as long as the script has not yet sent any other information to the client.

The script reads the current headers using the “apache_request_headers” function and checks whether an authorization header field exists which starts with the string “NTLM”. If this is not the case, authentication is requested.

In the second step, which is carried out after the client has been authenticated by NTLM, the client’s computername is read out, now.

$auth = $headers['AUTHORIZATION'];

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

        if ($msg[8] == "x01") {
                $msg2 = "NTLMSSPx00x02"."x00x00x00x00". 
                        "x00x00x00x00". 
                        "x01x02x81x01". 
                        "x00x00x00x00x00x00x00x00". 
                        "x00x00x00x00x00x00x00x00". 
                        "x00x00x00x00x30x00x00x00"; 

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

After the script has run through, the client hostname of the script caller (or better said of the website visitor) can be found in the variable “$workstation”.

Finally, I would like to express my special thanks to Loune, on whose work this blogpost has emerged.

Leave a comment

Please be polite. We appreciate that. Your email address will not be published and required fields are marked