Simple lightweight NTLM in PHP

Many months ago I made a PHP script that could read NTLM authentication information from your browser. What’s NTLM? Basically, if you’re using Microsoft Windows, your browser can automatically send your windows login information to a website (if you agree to it). This means that without needing to enter additional username or passwords, you can be authenticated at the website you’re visiting. This is quite convenient especially for company intranets. NTLM should work with all major browsers (Internet Explorer, Firefox and Opera).

The PHP code I wrote is simple and can be inserted into the top of any PHP script. The key output is $user $domain $workstation, which is the information advertised by the user. Be warned though, the script does NOT authenticate the user and merely assumes that the user is who they say they are. This is akin to a user entering only a username with no password required. I plan to add password/hash verification possibly in conjuction with samba in the future.

A limitation is that the PHP script relies on apache_request_headers() which is only available if you run PHP as a apache module. (Update 2010, newer code doesn’t have this issue)

[php]
<?php

// loune 25/3/2006, updated 22/08/2009
// For more information see:
// http://siphon9.net/loune/2007/10/simple-lightweight-ntlm-in-php/
//
// This script is obsolete, you should see
// http://siphon9.net/loune/2009/09/ntlm-authentication-in-php-now-with-ntlmv2-hash-checking/
//

// NTLM specs http://davenport.sourceforge.net/ntlm.html

$headers = apache_request_headers();

if (!isset($headers[‘Authorization’])){
header(‘HTTP/1.1 401 Unauthorized’);
header(‘WWW-Authenticate: NTLM’);
exit;
}

$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". // target name len/alloc
"\x00\x00\x00\x00". // target name offset
"\x01\x02\x81\x00". // flags
"\x00\x00\x00\x00\x00\x00\x00\x00". // challenge
"\x00\x00\x00\x00\x00\x00\x00\x00". // context
"\x00\x00\x00\x00\x00\x00\x00\x00"; // target info len/alloc/offset

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 $domain/$workstation";
}
}

?>
[/php]

If you try the script in Firefox (on windows), you will notice that you get prompted for a username and password when encountering an NTLM challenge. This is because sending your windows credentials to any unscrupulous website poses a real security risk. To make it automatically use your windows credentials for sites you trust, you can add the website to a whitelist.

The whitelist is located at Firefox’s about:config (type that into the address bar), which allows the editing of all of the browser’s preferences. Find the preference entry network.automatic-ntlm-auth.trusted-uris, double click on it and type the hostname of the site (ie http://www.abc.com) that you want in your whitelist. Multiple entries are seperated by commas. After doing that, Firefox should send your windows creds automatically.

Update 20/09/2009. The above script is outdated, anyone wishing to use NTLM should see the new post: Part 2 – Now with hash checking

63 thoughts on “Simple lightweight NTLM in PHP

  1. Pingback: How can I implement single sign-on (SSO) using Microsoft AD for an internal PHP app? | Questions

  2. ulrischa

    Hi, this is a great script. By far the only one I found for the usage of getting the windows username via server-side.
    My Problem is, that IE always shows the page: “Internet Explorer cannot display the webpage”.
    Server and client are in a company network.
    I was able to find out, that the problem occurs when the message 2 header is sent.
    Are there any configurations necessary on the client side except the entry of the adress in the intranet-zone sites list and ntlm enabled?

  3. Loune Post author

    @ulrischa the only thing I can think of is that the NTLM challenge-response should be done in a single HTTP connection/session. Did you check if you have configured the web server to allow keep-alive connections?

  4. ulrischa

    Hi loune. Yes keep Alive is activated. Could it be that ntml is deactivated by group policies in the client browser?

  5. Shiv

    Hi Loune, @ulrischa,

    My problem exactly matches that of @ulrischa. I have made sure that keep alive is activated on the Server as well. On top of that the main catch is that this works for some users and not of others.

    @ulrischa Were you able to solve your issue?

    Thanks in advance,
    Shiv

  6. Pingback: Client-Hostname in PHP auslesen - code-bude.net

  7. Pingback: How to read client hostname in PHP - en.code-bude.net

  8. bsmith

    Shiv or Ulrischa were either of you able to figure out what was going on? I have the same issue. It works for some and then others get the cannot display the webpage.

  9. ulrischa

    Hi,
    I was not able to solve the problem. One more point, that could cause the problem: Traffic goes throug a proxy. Could this cause the problem?

  10. Lorenz

    Hello sir @Loune,

    I’d like to ask for an assistance.

    in my development environment, im using slim and when im using non-domain account, it doesn’t pops up the auth login.
    but in test environment, it uses pure rest api php, and it pops up the auth login when using non-domain.

    I am only requiring to get the username, domain and workplace. and required to re-enter password only thru LDAP.
    So, how do i disable the pop up authentication for test environment?

    Thank you in advance.

  11. Mauro

    Hello @Loune! This saved me to do tons of work. But I have a question: how can I prevent this code from a simple CSRF? If a paste an ajax code in the browser’s console with any valid username, it will allow me to log in…

    Thanks in advance!
    Mauro

Leave a Reply

Your email address will not be published. Required fields are marked *