The theoretical description of LDAP is in the article Directory Services and LDAP. If we look at the issue very roughly, the directory is a database and LDAP are functions for accessing the database. Therefore, there's no problem in the theoretical use of LDAP from PHP. For PHP, there's an extension php_ldap that implements LDAP operations.
LDAP
To enable LDAP in PHP on the Windows operating system, we only need to turn on the PHP extension php_ldap. We do this by adding a line to the extension section in the php.ini file (or uncommenting it).
extension=php_ldap.dll
Then we can easily access LDAP. The description of functions is in the manual LDAP Functions, all 9 standard operations from LDAP v3 are implemented here. Some are divided into multiple functions and there are a number of additional functions for easier use. I'll briefly mention the basic functions.
ldap_connect()- Creates a connection to the specified LDAP server, returns a link ID or false. If not specified, the standard port 389 is used for LDAP.
ldap_set_option()- Sets the value of a specified property, such as the LDAP version.
ldap_bind()- Binds to the directory using the link ID. Authentication is performed using a name and password; if not provided, it attempts an anonymous connection. It usually behaves this way even if only the name is filled in and not the password. The server then often returns true, this happens even in the case of AD, even if anonymous connection is not allowed (and further access to the directory is not functional). The name should be entered using DN (CN=User,OU=Users,DC=company,DC=local), but for AD, entering only username (user) or UPN (user@company.local) also works.
ldap_search()- The search operation from LDAP. We specify the starting point of the search and filter, other parameters are optional. Returns a result ID or false.
ldap_get_entries()- One of the ways to get records returned by the search function (and others). Returns a multidimensional array where individual records are and for each record its attributes.
ldap_add()- Inserts a new record with the given DN and specified attributes.
ldap_compare()- Compares an attribute with a value for a given record.
ldap_close()- Correctly terminates the session, it's just an alias to ldap_unbind().
I'm also providing a simple example that connects to AD under a user and performs a simple search and listing of a few values. For proper use, handling of many events etc. is missing.
<?php
function ldapQuery($filter, $baseDN = "OU=company,DC=company,DC=local", $server = "192.168.0.1") {
$ldapCon = ldap_connect($server);
if($ldapCon) {
ldap_set_option($ldapCon, LDAP_OPT_PROTOCOL_VERSION, 3); // AD supports LDAPv3
ldap_set_option($ldapCon, LDAP_OPT_REFERRALS, 0); // it's recommended to turn off referrals for AD
$res = ldap_bind($ldapCon, "CN=User,OU=Users,DC=company,DC=local", "Password");
if($res === false) { echo "<p>Authentication failed.</p>"; ldap_close($ldapCon); return false; }
$rec = ldap_search($ldapCon, $baseDN, $filter);
echo "<p>Number of records found: " . ldap_count_entries($ldapCon, $rec) . "</p>";
$info = ldap_get_entries($ldapCon, $rec);
for($i=0; $i<$info["count"]; $i++) {
echo "DN: ".iconv("UTF-8", "ISO-8859-2", $info[$i]["dn"])."<br />";
echo "First CN: ".iconv("UTF-8", "ISO-8859-2", $info[$i]["cn"][0])."<br />";
echo "First email: ".$info[$i]["mail"][0]."<br /><hr />";
}
ldap_close($ldapCon);
} else { echo "<p>Failed to connect to LDAP server.</p>"; ldap_close($ldapCon); return false; }
}
ldapQuery("(memberOf=CN=Group,OU=company,DC=company,DC=local)");
?>
Note: Directory services use UTF-8 encoding, so if we have a different one on the page, we need to convert the values.
Note: If we use diacritics in names (in DN) in AD, we can use characters without diacritics in commands (and then we don't have to deal with the encoding problem).
LDAPS
In normal use of LDAP, communication between the web server and the directory server (I'll consider AD) takes place in an open form and when communication is intercepted, everything is readable. The greatest danger is with the authentication of bind operations.
If authentication is our only problem, we can use the SASL authentication method. If we need to secure all data, we need to encrypt all communication, which we can do either using SSL or TLS. I'll focus on SSL. LDAP that runs over SSL is referred to as LDAPS.
Note: If there's still some important data (for example, user authentication to AD) being sent from the client to the web server, it's good to encrypt this communication as well using SSL (HTTPS).
How to get LDAPS working
- LDAPS uses the same library as LDAP, so first we need to enable the
php_ldap.dllextension - Because we want to use SSL, we need to get Apache working with SSL, which is described in the article Apache server (EasyPHP) with SSL on Windows.
- We must have LDAPS working on the directory server, so the AD server must have a certificate.
- The
php_ldap.dlllibrary has a hard-coded path to its configuration file, which isC:\openldap\sysconf\ldap.conf(in Linux/etc/openldap/ldap.conf). So we need to create these directories and a file in them, into which we insert the following line
TLS_REQCERT never // the server certificate is not checked
And that's all, now we can use encrypted LDAPS. To use LDAPS in PHP, it's enough to use the server address ldaps://192.168.0.1 in the ldap_connect function, or specify the standard port 636 ldap_connect("192.168.0.1", 636).
The above procedure is simplified, we don't use a client certificate and we don't check the server certificate. Most guides on the internet contain steps where the root authority certificate, from which the directory server has its certificate, is downloaded. This is converted to PEM format using OpenSSL and added to the ldap.conf configuration file using the TLS_CACERT parameter.
Note: I encountered a problem when Apache was starting and php_ldap.dll was trying to load the certificate, the whole server crashed. Overwriting the php_ldap.dll library from the latest version of PHP (php-4.4.7-Win32.zip) helped.
Debugging
To solve problems with LDAP, we can turn on debug mode. We do this by entering the following command into the code before ldap_connect.
ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);
Subsequently, all operations performed with LDAP are written in detail to the errors.log file of the Apache server.
For example, if we use LDAPS and don't have the certificate correctly or don't use TLS_REQCERT never (for instance when ldap.conf doesn't exist at all), the following error appears in the log and the bind doesn't occur.
TLS certificate verification: Error, unable to get local issuer certificate TLS trace: SSL3 alert write:fatal:unknown CA
If we don't turn on debug, we won't find out why the bind failed.
Pekny clanek, diky za nej ;-)
Pekne clanky, ale nerozumim tomuto:
3.Musíme mít zprovozněné LDAPS na adresářovém serveru, takže AD server musí mít certifikát.
a pak kdyz pouziji:
"TLS_REQCERT never"
tak ctu "nekontroluje se certifikát serveru"
To znamena, ze AD server nemusi mit certifikat pri tomto nastaveni?
respond to [2]LaYosH: Aby mohla být komunikace šifrovaný (při SSL nebo TLS) tak se využívá certifikát a jeho klíč. Takže vždy tam ten certifikát musí být.
Druhá věc je, že certifikát se dá použít i k ověření, že server je opravdu ten, za který se vydává. To se provádí na straně klienta, ale tuto kontrolu nemusím použít.
Ahoj,
řeším také tento problém.
Mám XAMPP, kde SSL funguje (https://localhost běží).
Ale když se zkusím připojit na ldaps, tak dostanu přesně tu chybu, která je zmíněná na konci textu.
Žádný z návodů, které jsem našel nezabral.
Neví někdo, co s tím? (případně: otto.kovarik@gmail.com)
Díky, to je super článek. S Active Directory všeobecně jsem se pral hrozně dlouhou dobu než sem na je nějak "zdolal":-) Jinak PHP mi přijde jako jeden z nejlepších jazyků, nejen srozumitelně, ale i v rámci nějakýho uplatnění. A nejlépe uplatnění v cizině. Zkoušel jsem najít nějaké weby, které se tím specializují, nevíte nějaké osvědčené? Mě říkal kámoš o itprace-nemecko.cz/,prý mu tam sehnali nějakou brigádu, akorát jsem se ještě nedostal k tomu, abych tam napsal životopis... :D (snad neva ten odkaz, nemyslel jsem to jako spam, spíš jestli s touhle konkrétní firmou má někdo nějaké zkušenosti)