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.

You can get the source of the PHP code here.

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

16 Responses to “Simple lightweight NTLM in PHP”

  1. [...] the rest of this great post here [...]

  2. Andrew says:

    This works great, but how do I log the user off and so that the NTML dialog re-appears again?

  3. Kruno says:

    is this working on IE6 and IE7? When looking tcp dump i see IE abandoning request just after sending x03 message, and before receiving 200 OK response.
    In firefox it is working fine. any taughts?

  4. Loune says:

    Andrew – the point of NTLM is that you don’t need to enter the username/password. It reads it from the current user on windows. Otherwise, you can possibly send a 401 HTTP status code to clear the authenticated session.

    Kruno – It works fine for IE6 for me. Have you checked that the site you’re trying it with is trusted or on local intranet zone? Also I’ve heard that the whole transaction needs to be on one connection so another thing to check is to see if persistent connections are enabled.

  5. Matt says:

    This is great and works fine – I can take the username and cross reference users in a MySQL database to determine what to display in my php form, but when I submit the form, it doesn’t appear to be passing the elements in the REQUEST headers. Is that a function of the NTLM piece? It works fine without it included.

  6. Paul says:

    Hi – any reason why this would work fine on my http server, but not a https one?

  7. Gary says:

    When I check your code against the Microsoft doco “NT LAN Manager (NTLM) Authentication Protocol Specification” http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NLMP%5D.pdf
    it doesn’t seem to match. I’m having some trouble with subsequent pages POSTing data back to the server and am trying to debug it. Do you have a detailed breakdown of the NTLMSSP challenge (that you assigning to $msg2)?

  8. Eric Z says:

    Cool function, Did you ever figure out that hash check version?

  9. Loune says:

    @Gary – you’re right $msg2 is missing 3 bytes, it’s now fixed.

    @Eric Z – See the new php ntlm library with hash checking:
    http://siphon9.net/loune/2009/09/ntlm-authentication-in-php-now-with-ntlmv2-hash-checking/

  10. David says:

    I wanted to use this for our Intranet, not for authentication but to personalise the site for our users. So the hash check would sorta be over kill. This lightweight version works perfect… and here’s the but…

    BUT, all forms on the intranet that are set to method=”post” stop working and the $_POST array is totally empty. This is not the case for form set as method=”get”.

    As soon as I disable the ntlm authorisation all the forms are working again. Odd thing is that some of these forms are located in pages within a different directory even i.e. ntlm auth happens on index.php and subsequently a form located in /admin/docmanager.php stops working.

    I am at wits end on this one and have scoured Google from end to end for a solution…. Any thoughts?

    P.S. This even happens on a fresh install of LAMP.

  11. Loune says:

    This is probably due to the 3-way handshake (negotiate, challenge, response) the ntlm script performs. A post would be eaten up by the negotiate step. Ideally you should only ntlm authenticate the first time in a browser session and after that, you set a cookie and don’t ntlm authenticate until the next session ie:

    $user = $_COOKIE['username'];
    if (empty($user)) {
    // the ntlm.php stuff
    // straight after print “You are $user…, add:
    setcookie(‘username’, $user);
    }

    This would work provided that the user has cookies enabled and that your first request in that session isn’t a post.

  12. David says:

    Nope, still not working.

    Well…. it *sorta* works. Once the cookie has been set and you close and reopen the browser then the post forms work. I am guessing this is due to the authentication being skipped as $username is not empty.

    Really, REALLY baffling! —- Ohhh look! A grey hair… YAY!

  13. Loune says:

    I’ve just quickly mocked up an example and it works fine. There was a mistake in my original snippet in that $username variable should really just be named $user as per my ntlm code. I’ve fixed that now.

    My testing was in firefox 3.0 and IE8. Could it be some quirk or setting with your browser regarding cookies? I can post my example up if you want.

  14. David says:

    Yeah that’s be nice. I wasn’t using a cookie until suggested it (there wasn’t much of interest that I could put in the cookie before this authentication snippet) so I doubt it is a problem with a cookie.

    Strange thing is it *does* work for Firefox, not a single problem. The problems occur mainly in IE7 & IE8 (though occasionally IE8 works but without any changes, deletions or anything stops working).

    Now as much as I would LOVE to roll out FF3 within the company the directors would have an absolute heart attack as it can’t be simply managed from a central location…. Hey hang on, now there is an opportunity for a KICKASS addon and an server app to manage it, might boost adoption, but I digress…

  15. Ira says:

    David:

    I had the same problem with it causing the post data to be empty, and I spent all day trying to figure out a solution. We solved the problem by simply adding that same code(posted by Loune) into every php file that is doing the “$variable = $_POST['name'].”

Leave a Reply