forked from github/codeql
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Query to detect LDAP injections in Java
Add help
- Loading branch information
Showing
5 changed files
with
152 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 changes: 17 additions & 0 deletions
17
java/ql/src/Security/CWE/CWE-90/LdapInjectionUnboundId.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |