Ldap Injection in Gin
How Ldap Injection Manifests in Gin
LDAP injection in Gin applications typically occurs when user-supplied input is concatenated directly into LDAP filter strings without proper sanitization. This vulnerability allows attackers to manipulate LDAP queries, potentially bypassing authentication, accessing unauthorized data, or causing denial of service.
In Gin applications, LDAP injection often appears in authentication middleware, user directory services, or any endpoint that queries LDAP directories like Active Directory. The vulnerability manifests when developers use string concatenation to build LDAP filters instead of parameterized queries.
func authHandler(c *gin.Context) {
username := c.Query("username")
password := c.Query("password")
// Vulnerable: direct string concatenation
filter := "(&(objectClass=user)(sAMAccountName=" + username + ")(userPassword=" + password + "))"
searchResult, err := ldap.SearchWithFilter(conn, filter)
if err != nil || len(searchResult.Entries) == 0 {
c.JSON(401, gin.H{"error": "Invalid credentials"})
return
}
c.JSON(200, gin.H{"message": "Authenticated"})
}This code is vulnerable because an attacker can provide input like username=admin)(|(objectClass=* which would create a filter that always returns true, bypassing authentication entirely.
Another common Gin-specific pattern occurs when using LDAP for authorization checks:
func checkPermission(c *gin.Context) {
userID := c.Query("user_id")
resource := c.Query("resource")
// Vulnerable: user input in LDAP filter
filter := "(&(objectClass=user)(uid=" + userID + ")(memberOf=cn=" + resource + ",ou=groups,dc=example,dc=com))"
result, _ := ldap.SearchWithFilter(conn, filter)
if len(result.Entries) > 0 {
c.JSON(200, gin.H{"authorized": true})
} else {
c.JSON(403, gin.H{"authorized": false})
}
}Attackers can exploit this by injecting LDAP operators like *, |, &, or parentheses to manipulate the query logic.
Gin-Specific Detection
Detecting LDAP injection in Gin applications requires both static code analysis and dynamic testing. Here are Gin-specific detection methods:
Static Analysis: Search for LDAP filter construction patterns in your Gin handlers:
# Look for string concatenation with LDAP operators
grep -r "ldap.*filter.*+.*" --include="*.go" --include="*.c" .
grep -r "sAMAccountName.*" --include="*.go" --include="*.c" .
grep -r "userPassword.*" --include="*.go" --include="*.c" .Dynamic Testing with middleBrick: middleBrick's black-box scanning can detect LDAP injection vulnerabilities in running Gin APIs without requiring source code access. The scanner tests for LDAP-specific injection patterns by sending crafted payloads to authentication and directory service endpoints.
Manual Testing: Test your Gin endpoints with these LDAP injection payloads:
# Authentication bypass
curl "http://localhost:8080/auth?username=admin)(|(objectClass=*&password=anything"
# Information disclosure
curl "http://localhost:8080/search?uid=admin)(cn=*")
# Denial of service
curl "http://localhost:8080/search?uid=*)(objectClass=*)"
middleBrick CLI Integration: Scan your Gin API directly from the terminal:
npm install -g middlebrick
middlebrick scan https://api.yourapp.com/auth
The scanner will test for LDAP injection among 12 other security checks, providing a risk score and specific findings about any LDAP vulnerabilities detected.
Gin-Specific Remediation
Remediating LDAP injection in Gin applications requires using parameterized LDAP queries instead of string concatenation. Here are Gin-specific fixes:
Using LDAP Filter Escaping: The Go LDAP library provides filter escaping functions:
import "github.com/go-ldap/ldap/v3"
func authHandler(c *gin.Context) {
username := c.Query("username")
password := c.Query("password")
// Secure: escaped filter construction
escapedUser := ldap.EscapeFilter(username)
escapedPass := ldap.EscapeFilter(password)
filter := fmt.Sprintf("(&(objectClass=user)(sAMAccountName=%s)(userPassword=%s))",
escapedUser, escapedPass)
searchResult, err := ldap.SearchWithFilter(conn, filter)
if err != nil || len(searchResult.Entries) == 0 {
c.JSON(401, gin.H{"error": "Invalid credentials"})
return
}
c.JSON(200, gin.H{"message": "Authenticated"})
}Using LDAP Search Requests: For more complex queries, use structured search requests:
func checkPermission(c *gin.Context) {
userID := c.Query("user_id")
resource := c.Query("resource")
// Secure: parameterized search
searchRequest := ldap.NewSearchRequest(
"ou=users,dc=example,dc=com",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=user)(uid=%s)(memberOf=cn=%s,ou=groups,dc=example,dc=com))",
ldap.EscapeFilter(userID), ldap.EscapeFilter(resource)),
[]string{"dn"},
nil,
)
result, err := ldap.Search(searchRequest)
if err != nil {
c.JSON(500, gin.H{"error": "LDAP error"})
return
}
c.JSON(200, gin.H{"authorized": len(result.Entries) > 0})
}Input Validation Middleware: Add Gin middleware to validate LDAP-specific characters:
func ldapInputValidationMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Check for LDAP injection characters
if strings.ContainsAny(c.Query("username"), "*()&|\"\n\r\0") {
c.JSON(400, gin.H{"error": "Invalid input"})
c.Abort()
return
}
c.Next()
}
}
// Use in routes
router.Use(ldapInputValidationMiddleware())
router.POST("/auth", authHandler)
Testing with middleBrick: After implementing fixes, use middleBrick to verify remediation:
middlebrick scan https://api.yourapp.com/auth --retest
The scanner will attempt LDAP injection attacks again and confirm whether the vulnerabilities have been successfully mitigated.