mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Query to detect LDAP injections in Java
Add help
This commit is contained in:
62
java/ql/src/Security/CWE/CWE-90/LdapInjection.qhelp
Normal file
62
java/ql/src/Security/CWE/CWE-90/LdapInjection.qhelp
Normal file
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>If an LDAP query is built using string concatenation, and the
|
||||
components of the concatenation include user input, a user
|
||||
is likely to be able to run malicious LDAP queries.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>If user input must be included in an LDAP query, it should be escaped to
|
||||
avoid a malicious user providing special characters that change the meaning
|
||||
of the query. If possible build the LDAP query (or search filter / DN) using your
|
||||
framework helper methods to avoid string concatenation, or escape user input
|
||||
using the right LDAP encoding method, for example encodeForLDAP from OWASP ESAPI,
|
||||
LdapEncoder from Spring LDAP or Filter.encodeValue from UnboundID library.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following examples, the code accepts an "organization name" and a "username"
|
||||
from the user, which it uses to query LDAP.</p>
|
||||
|
||||
<p>The first example concatenates the unvalidated and unencoded user input directly
|
||||
into both the DN (Distinguished Name) and the search filter used for the LDAP query.
|
||||
A malicious user could provide special characters to change the meaning of these
|
||||
queries, and search for a completely different set of values. The LDAP query is executed
|
||||
using Java JNDI API.
|
||||
</p>
|
||||
|
||||
<p>The second example uses the OWASP ESAPI library to encode the user values
|
||||
before they are included in the DN and search filters. This ensures the meaning of
|
||||
the query cannot be changed by a malicious user.</p>
|
||||
|
||||
<sample src="LdapInjectionJndi.java" />
|
||||
|
||||
<p>The third example uses Spring LdapQueryBuilder to build LDAP query. In addition to
|
||||
simplifying building of complex search parameters, it also provides proper escaping of any
|
||||
unsafe characters in search filters. DN is built using LdapNameBuilder, which also provides
|
||||
proper escaping.</p>
|
||||
|
||||
<sample src="LdapInjectionSpring.java" />
|
||||
|
||||
<p>The fourth example uses UnboundID Filter and DN classes to construct safe filter and
|
||||
base DN.</p>
|
||||
|
||||
<sample src="LdapInjectionUnboundId.java" />
|
||||
|
||||
<p>The fifth example shows how to build safe filter and DN using Apache LDAP API.</p>
|
||||
|
||||
<sample src="LdapInjectionApache.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/LDAP_Injection_Prevention_Cheat_Sheet.html">LDAP Injection Prevention Cheat Sheet</a>.</li>
|
||||
<li>OWASP: <a href="https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java">Preventing LDAP Injection in Java</a>.</li>
|
||||
<li>OWASP ESAPI: <a href="https://owasp.org/www-project-enterprise-security-api/">OWASP ESAPI</a>.</li>
|
||||
<li>Spring LdapQueryBuilder doc: <a href="https://docs.spring.io/spring-ldap/docs/current/apidocs/org/springframework/ldap/query/LdapQueryBuilder.html">LdapQueryBuilder</a>.</li>
|
||||
<li>Spring LdapNameBuilder doc: <a href="https://docs.spring.io/spring-ldap/docs/current/apidocs/org/springframework/ldap/support/LdapNameBuilder.html">LdapNameBuilder</a>.</li>
|
||||
<li>UnboundID: <a href="https://ldap.com/2018/05/04/understanding-and-defending-against-ldap-injection-attacks/">Understanding and Defending Against LDAP Injection Attacks</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
22
java/ql/src/Security/CWE/CWE-90/LdapInjectionApache.java
Normal file
22
java/ql/src/Security/CWE/CWE-90/LdapInjectionApache.java
Normal file
@@ -0,0 +1,22 @@
|
||||
import org.apache.directory.ldap.client.api.LdapConnection;
|
||||
import org.apache.directory.api.ldap.model.name.Dn;
|
||||
import org.apache.directory.api.ldap.model.name.Rdn;
|
||||
import org.apache.directory.api.ldap.model.message.SearchRequest;
|
||||
import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
|
||||
import static org.apache.directory.ldap.client.api.search.FilterBuilder.equal;
|
||||
|
||||
public void ldapQueryGood(HttpServletRequest request, LdapConnection c) {
|
||||
String organizationName = request.getParameter("organization_name");
|
||||
String username = request.getParameter("username");
|
||||
|
||||
// GOOD: Organization name is encoded before being used in DN
|
||||
Dn safeDn = new Dn(new Rdn("OU", "People"), new Rdn("O", organizationName));
|
||||
|
||||
// GOOD: User input is encoded before being used in search filter
|
||||
String safeFilter = equal("username", username);
|
||||
|
||||
SearchRequest searchRequest = new SearchRequestImpl();
|
||||
searchRequest.setBase(safeDn);
|
||||
searchRequest.setFilter(safeFilter);
|
||||
c.search(searchRequest);
|
||||
}
|
||||
34
java/ql/src/Security/CWE/CWE-90/LdapInjectionJndi.java
Normal file
34
java/ql/src/Security/CWE/CWE-90/LdapInjectionJndi.java
Normal file
@@ -0,0 +1,34 @@
|
||||
import javax.naming.directory.DirContext;
|
||||
import org.owasp.esapi.Encoder;
|
||||
import org.owasp.esapi.reference.DefaultEncoder;
|
||||
|
||||
public void ldapQueryBad(HttpServletRequest request, DirContext ctx) throws NamingException {
|
||||
String organizationName = request.getParameter("organization_name");
|
||||
String username = request.getParameter("username");
|
||||
|
||||
// BAD: User input used in DN (Distinguished Name) without encoding
|
||||
String dn = "OU=People,O=" + organizationName;
|
||||
|
||||
// BAD: User input used in search filter without encoding
|
||||
String filter = "username=" + userName;
|
||||
|
||||
ctx.search(dn, filter, new SearchControls());
|
||||
}
|
||||
|
||||
public void ldapQueryGood(HttpServletRequest request, DirContext ctx) throws NamingException {
|
||||
String organizationName = request.getParameter("organization_name");
|
||||
String username = request.getParameter("username");
|
||||
|
||||
// ESAPI encoder
|
||||
Encoder encoder = DefaultEncoder.getInstance();
|
||||
|
||||
// GOOD: Organization name is encoded before being used in DN
|
||||
String safeOrganizationName = encoder.encodeForDN(organizationName);
|
||||
String safeDn = "OU=People,O=" + safeOrganizationName;
|
||||
|
||||
// GOOD: User input is encoded before being used in search filter
|
||||
String safeUsername = encoder.encodeForLDAP(username);
|
||||
String safeFilter = "username=" + safeUsername;
|
||||
|
||||
ctx.search(safeDn, safeFilter, new SearchControls());
|
||||
}
|
||||
17
java/ql/src/Security/CWE/CWE-90/LdapInjectionSpring.java
Normal file
17
java/ql/src/Security/CWE/CWE-90/LdapInjectionSpring.java
Normal file
@@ -0,0 +1,17 @@
|
||||
import static org.springframework.ldap.query.LdapQueryBuilder.query;
|
||||
import org.springframework.ldap.support.LdapNameBuilder;
|
||||
|
||||
public void ldapQueryGood(@RequestParam String organizationName, @RequestParam String username) {
|
||||
// GOOD: Organization name is encoded before being used in DN
|
||||
String safeDn = LdapNameBuilder.newInstance()
|
||||
.add("O", organizationName)
|
||||
.add("OU=People")
|
||||
.build().toString();
|
||||
|
||||
// GOOD: User input is encoded before being used in search filter
|
||||
LdapQuery query = query()
|
||||
.base(safeDn)
|
||||
.where("username").is(username);
|
||||
|
||||
ldapTemplate.search(query, new AttributeCheckAttributesMapper());
|
||||
}
|
||||
17
java/ql/src/Security/CWE/CWE-90/LdapInjectionUnboundId.java
Normal file
17
java/ql/src/Security/CWE/CWE-90/LdapInjectionUnboundId.java
Normal file
@@ -0,0 +1,17 @@
|
||||
import com.unboundid.ldap.sdk.LDAPConnection;
|
||||
import com.unboundid.ldap.sdk.DN;
|
||||
import com.unboundid.ldap.sdk.RDN;
|
||||
import com.unboundid.ldap.sdk.Filter;
|
||||
|
||||
public void ldapQueryGood(HttpServletRequest request, LDAPConnection c) {
|
||||
String organizationName = request.getParameter("organization_name");
|
||||
String username = request.getParameter("username");
|
||||
|
||||
// GOOD: Organization name is encoded before being used in DN
|
||||
DN safeDn = new DN(new RDN("OU", "People"), new RDN("O", organizationName));
|
||||
|
||||
// GOOD: User input is encoded before being used in search filter
|
||||
Filter safeFilter = Filter.createEqualityFilter("username", username);
|
||||
|
||||
c.search(safeDn.toString(), SearchScope.ONE, safeFilter);
|
||||
}
|
||||
Reference in New Issue
Block a user