Prevention of LDAP injection attacks
public class LdapController : Controller
{
private readonly LdapConnection _connection;
public LdapController(LdapConnection connection)
{
_connection = connection;
}
[HttpPost]
public IActionResult Authenticate(string username, string password)
{
var searchFilter = $"(&(objectClass=user)(sAMAccountName={username}))";
var searchRequest = new SearchRequest("dc=example,dc=com", searchFilter, SearchScope.Subtree);
var searchResponse = (SearchResponse)_connection.SendRequest(searchRequest);
if (searchResponse.Entries.Count > 0)
{
var user = searchResponse.Entries[0];
var networkCredential = new NetworkCredential(user.DistinguishedName, password);
_connection.Bind(networkCredential);
return Ok();
}
return Unauthorized();
}
}
The above code is an example of an LDAP injection vulnerability in a C# backend application using ASP.NET Core.
In this code, the
Authenticate
method is used to authenticate a user against an LDAP server. The method takes a
username
and
password
as parameters, which are directly injected into the LDAP query without any sanitization or validation. The LDAP query is constructed by concatenating the
username
directly into the search filter string:
var searchFilter = $"(&(objectClass=user)(sAMAccountName={username}))";
username
. For instance, an attacker could provide a
username
like
admin)(objectClass=*)
, which would result in the following search filter:
"(&(objectClass=user)(sAMAccountName=admin)(objectClass=*))"
password
is used directly to bind to the LDAP connection without any validation:
var networkCredential = new NetworkCredential(user.DistinguishedName, password);
_connection.Bind(networkCredential);
password
to authenticate as any user, depending on the LDAP server configuration.
public class LdapController : Controller
{
private readonly LdapConnection _connection;
public LdapController(LdapConnection connection)
{
_connection = connection;
}
[HttpPost]
public IActionResult Authenticate(string username, string password)
{
// Validate and sanitize user input
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
return BadRequest("Username and password must not be empty");
}
// Use parameterized queries to prevent LDAP injection
var searchFilter = new LdapFilterBuilder()
.Equals("objectClass", "user")
.And()
.Equals("sAMAccountName", username)
.Build();
var searchRequest = new SearchRequest("dc=example,dc=com", searchFilter, SearchScope.Subtree);
var searchResponse = (SearchResponse)_connection.SendRequest(searchRequest);
if (searchResponse.Entries.Count > 0)
{
var user = searchResponse.Entries[0];
var networkCredential = new NetworkCredential(user.DistinguishedName, password);
_connection.Bind(networkCredential);
return Ok();
}
return Unauthorized();
}
}
The original code was vulnerable to LDAP injection because it was building LDAP queries using untrusted data. This could allow an attacker to modify the query, potentially leading to unauthorized access or data exposure.
The fixed code addresses this vulnerability by implementing several security measures:
1. Input Validation: The code checks if the username and password are null or empty. If they are, it returns a bad request. This prevents null or empty values from being used in the LDAP query.
2. Parameterized Queries: The code uses a
LdapFilterBuilder
to build the LDAP query. This class creates parameterized queries, which means that the username is treated as a literal string, not part of the query. This prevents an attacker from modifying the query.
These changes ensure that the LDAP query is built safely, preventing LDAP injection attacks. Additionally, it's recommended to implement strong authentication mechanisms, access controls, and regularly update and patch the LDAP server to further enhance the security.