Fixing LDAP injection
The Light Directory Access Protocol (LDAP) is a standard protocol used to access directory services such as Microsoft's Active Directory and Apache Directory. Web applications use LDAP to search the directory server to get users and group information, which also serves as a means of authentication. This retrieval of data from the web application to the LDAP directory server is possible because of the LDAP query language and its filters. Developers write code to compose these queries. Like any other dynamic query construction, this method can open the code to injection, particularly LDAP injection, when the concatenated user-controlled input is not validated or sanitized.
In this recipe, we will identify the LDAP injection vulnerability in code and fix the security vulnerability.
Getting ready
Using Visual Studio Code, open the sample Online Banking app folder at \Chapter02\ldap-injection\before\OnlineBankingApp\
.
How to do it…
Let's take a look at the steps for this recipe:
- Launch Visual Studio Code and open the starting exercise folder by typing the following command:
code .
- Navigate to Terminal | New Terminal in the menu or simply press Ctrl + Shift + ' in Visual Studio Code.
- Type the following command in the terminal to build the sample app to confirm that there are no compilation errors:
dotnet build
- Open the
Services/LdapDirectoryService.cs
file and locate the vulnerable part of the code in theSearch(string userName)
method:public User Search(string userName) { using (DirectoryEntry entry = new DirectoryEntry(config.Path)) { entry.AuthenticationType = AuthenticationTypes.Anonymous; using (DirectorySearcher searcher = new DirectorySearcher(entry)) { searcher.Filter = "(&(" + UserNameAttribute + "=" + userName + "))"; searcher.PropertiesToLoad.Add (EmailAttribute); searcher.PropertiesToLoad.Add (UserNameAttribute); var result = searcher.FindOne(); // code removed for brevity
- To fix the LDAP injection vulnerability, refactor the code to include a whitelist validation of the
userName
parameter:public User Search(string userName) { if (Regex.IsMatch(userName, "^[a-zA-Z][a-zA-Z0- 9]*$")){ using (DirectoryEntry entry = new DirectoryEntry(config.Path)) { entry.AuthenticationType = AuthenticationTypes.Anonymous; using (DirectorySearcher searcher = new DirectorySearcher(entry)) { searcher.Filter = "(&(" + UserNameAttribute + "=" + userName + "))"; searcher.PropertiesToLoad.Add (EmailAttribute); searcher.PropertiesToLoad.Add (UserNameAttribute); var result = searcher.FindOne(); // code removed for brevity
Reusing the whitelisting technique through the use of regular expressions, we again utilize the IsMatch
method to ascertain whether the pattern matches the input. If the input does not match the regular expression, the input is then rejected.
How it works…
In our sample solution, we have a web page that allows an admin user to search for a specific user account using the search bar:
Entering a user ID and hitting the Search button will send an LDAP query to the LDAP directory service to search for a user that has the exact user ID:
Note
The steps on setting up your LDAP directory service are not provided in this book. Suppose you want a working directory server that runs in your local machine to work with the sample solution. In that case, I suggest you install ApacheDS and follow the steps from the Setting up an LDAP server for development/testing using Apache Directory Studio page in the official Crafter CMS documentation: https://docs.craftercms.org/en/3.1/developers/cook-books/how-tos/setting-up-an-ldap-server-for-dev.html.
Change the Ldap
entry in appsettings.json
if necessary:
"Ldap": {
"Path": "LDAP://localhost:10389/DC=example,DC=com",
"UserDomainName": "example"
},
As the Search
method is invoked, an LDAP query is dynamically composed, and a filter is concatenated with the value entered in the search textbox:
searcher.Filter = "(&(" + UserNameAttribute + "=" + userName + "))";
The userName
parameter is not sanitized or validated, and a bad actor can exploit this by injecting suspicious filters that could retrieve sensitive information from the LDAP directory server.
To mitigate this risk, we used Regex's IsMatch
method to add a whitelist validation approach. The conditional expression will only be equivalent to true if any of the characters in userName
are alphanumeric:
public User Search(string userName) { if (Regex.IsMatch(userName, "^[a-zA-Z][a-zA-Z0-9]*$")){ using (DirectoryEntry entry = new DirectoryEntry(config.Path)) { // code removed for brevity
Include as part of the overall secure coding strategy the implementation of a whitelist input validation to check user-controlled inputs, safeguarding your ASP.NET Core web application from LDAP injection attacks.