A few weeks ago I decided that it would be better if all the web applications we develop would log users in by using their Active Directory authentication details.
This makes things easier for our users, they don't need to remember their username and password for each application. They will most probably use the same details everywhere anyway, but there's no point in keeping this information in many places, is there?
Well this task seemed pretty SF when I got the idea, but with researching, reading and testing, I managed to make it work. So I use LDAP to connect to our AD server and because I use PHP on the server side, I use PHP's LDAP Functions which are not enabled by default.
The typical sequence of LDAP calls you will make in an application will follow this pattern:
ldap_connect() // establish connection to server
|
ldap_bind() // anonymous or authenticated "login"
|
ldap_search() // search for some information
|
ldap_close() // "logout"
For authentication a typical application will do this:
- connect to the server
- bind anonymously or with a predefined user made especially for authenticating
- search for the user name in the directory
- try to bind with the user name and password given and see if it works
- disconnect from the server
Well this approach is very common. Here's a code example. If you search on google for "php ldap authentication" you'll find many code samples that do it in a similar way.
Well, my approach is different. Why bind anonymously or with a special user name and password? Why not try binding directly with the given name and password? That's what I did and it worked, but I noticed a weird thing about the ldap_bind function that I didn't find in the documentation: if the user name doesn't exist in the directory, the function will bind automatically anonymously even though a password is given.
To make sure the user is really authenticated after a successful bind, do a search of the directory and see if the user name exists and of course if it belongs to the right branch of the directory tree. I think this is more efficient and as secure as the other approach. The other approach does one more bind without reason and if a user enters wrong login password a bind and a search is done anyway, in my approach binding fails and returns failure quicker. Also instead of getting the search results and then the right data out of the structure, I check only the number of results which should be one for a successful authentication.
I know, I know, I'm an optimization freak ;-)
I found another weird behaviour that needed some more research. I noticed that even though my login details were correct the search returned no results. Here's the comment that helped me get to the bottom of it. So I had to change the port I was connecting to, from 389 to 3268.
However I was wondering what did "search the entire tree" mean. I thought I wasn't searching the entire tree, I was searching only for users in a certain domain because I was specifying the DC attribute. Changing the port made everything work, but I just was a bit confused so I tried different filters for my search.
I finally found that if I include in my filter the OU (OrganisationalUnit) the search works fine even when connecting to the 389 port. Isn't that interesting? So what does exactly "search the entire tree" mean?
Here's my code, take a look, give me some comments.
$LDAPFail = true;
//ldap rdn or dn
$ldaprdn = $username."@mydomain.com";
// associated password
$ldappass = $password;
// connect to ldap server
@$ldapconn = ldap_connect("xxx.xxx.xxx.xxx","389");
if ($ldapconn)
{
//binding to ldap server
$ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass);
// verify binding
if ($ldapbind)
{
$dn = "OU=MyCompany_Users;DC=mydomain,DC=com";
$filter="(SAMAccountName=".$username.")";
$sr = ldap_search($ldapconn, $dn, $filter,Array("samaccountname"));
if(ldap_count_entries($ldapconn, $sr) == 1)
$LDAPFail = false;
}
ldap_close($ldapconn);
}
I hope this helps someone out there...
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment