Regex Dos in Gin
How Regex Dos Manifests in Gin
Regex DoS (Denial of Service) attacks exploit the worst-case performance of certain regular expressions, causing catastrophic backtracking that can bring down your Gin application. In Gin's routing layer, regex patterns are commonly used for path parameters, middleware matching, and request validation.
The most vulnerable pattern in Gin is the use of nested quantifiers without proper anchors. Consider this Gin route definition:
router.GET("/user/:id", func(c *gin.Context) {
// handler code
})While this looks safe, if you modify it to use regex capture groups with nested quantifiers, you create a vulnerability:
router.GET("/user/:id([a-z]+)*", func(c *gin.Context) {
// vulnerable handler
})The pattern [a-z]+ inside (...)* creates exponential backtracking. An attacker can send requests like:
GET /user/aaa... (thousands of 'a' characters)Gin's router will attempt to match this pattern, exploring every possible way to divide the string into groups, causing CPU usage to spike to 100%.
Another common Gin-specific scenario involves middleware that uses regex for path matching:
router.Use(gin.BasicAuth(gin.Accounts{
"admin": "password",
}))
// Problematic: unanchored regex in middleware
router.GET("/admin/*path", gin.BasicAuth(gin.Accounts{
"admin": "password",
}))The *path wildcard can be exploited when combined with poorly constructed regex patterns in custom middleware. Attackers can craft requests that force the router to evaluate thousands of backtracking paths before rejecting the request.
Gin's gin.Context.Param() method can also be a vector when combined with regex extraction:
router.GET("/search/:query", func(c *gin.Context) {
query := c.Param("query")
// If query is used in regex operations without validation
re := regexp.MustCompile("(a+)+b") // Vulnerable pattern
re.MatchString(query)
c.JSON(200, gin.H{"result": query})
})This creates a double vulnerability: the router processes the path, then your handler processes the parameter with a vulnerable regex.
Gin-Specific Detection
Detecting Regex DoS in Gin applications requires both static analysis and runtime monitoring. Start with code review focusing on these Gin-specific patterns:
import (
"regexp"
"github.com/gin-gonic/gin"
)
// Vulnerable: nested quantifiers without anchors
var badPattern = regexp.MustCompile("(a+)+b")
func main() {
router := gin.Default()
// Check for problematic patterns in route definitions
router.GET("/test/:id([a-z]+)*", vulnerableHandler)
// Check middleware regex usage
router.Use(authMiddleware())
router.Run(":8080")
}
func vulnerableHandler(c *gin.Context) {
id := c.Param("id")
// This will cause catastrophic backtracking
if badPattern.MatchString(id) {
c.JSON(200, gin.H{"status": "ok"})
}
}
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Check for unanchored patterns in path matching
path := c.Request.URL.Path
if match, _ := regexp.MatchString("(admin|user)+", path); match {
// Processing logic
}
}
}For automated detection, middleBrick's API security scanner includes specific checks for regex DoS patterns in Gin applications. It analyzes your OpenAPI/Swagger specs and runtime behavior to identify:
- Nested quantifiers like
(a+)+,(a*)*,(a+)* - Unanchored patterns that allow partial matches
- Exponential patterns like
(a+)+bor(a|a*)* - Backtracking-heavy alternations without proper ordering
middleBrick's scanner tests these patterns by sending crafted requests that trigger worst-case performance, measuring response times and CPU usage. The scanner specifically understands Gin's routing syntax and can identify vulnerable route patterns directly from your API definitions.
Runtime monitoring in production should track:
// Simple monitoring middleware for Gin
func regexDosMonitor() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
duration := time.Since(start)
// Flag requests taking suspiciously long
if duration > 500*time.Millisecond && c.Request.Method == "GET" {
log.Printf("Potential regex dos: %s %s took %v",
c.Request.Method, c.Request.URL.Path, duration)
}
}
}Integrate this with your Gin router to catch slow requests that might indicate regex exploitation attempts.
Gin-Specific Remediation
Remediating Regex DoS in Gin applications requires a multi-layered approach. Start by replacing vulnerable patterns with safer alternatives:
// Vulnerable pattern - DO NOT USE
badPattern := regexp.MustCompile("(a+)+b")
// Safe alternatives
// 1. Use possessive quantifiers (if supported) or atomic groups
// Go's regexp doesn't support possessive, so use alternatives:
// 2. Use bounded repetition with anchors
safePattern := regexp.MustCompile(`^a{1,100}b$`)
// 3. Use negative lookahead to prevent catastrophic backtracking
// This pattern matches 'a' followed by 'b' without backtracking
safePattern := regexp.MustCompile(`^(a+)b$`)
// 4. For complex patterns, use finite automata or state machines
// Instead of regex, use simple string operations when possible
func validateID(id string) bool {
if len(id) > 100 {
return false
}
// Simple character class check instead of regex
for _, char := range id {
if char < 'a' || char > 'z' {
return false
}
}
return true
}
// In your Gin handler
router.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
if !validateID(id) {
c.JSON(400, gin.H{"error": "invalid id"})
return
}
// Safe processing
})For route definitions in Gin, avoid complex regex patterns entirely:
// Instead of this (vulnerable)
router.GET("/user/:id([a-z]+)*", vulnerableHandler)
// Use simple path parameters
router.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
// Validate length and character set manually
if len(id) > 50 || !isValidID(id) {
c.JSON(400, gin.H{"error": "invalid id"})
return
}
c.JSON(200, gin.H{"id": id})
})
// Helper function for validation
func isValidID(id string) bool {
if len(id) == 0 || len(id) > 50 {
return false
}
for _, r := range id {
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') {
return false
}
}
return true
}For middleware that needs pattern matching, use pre-compiled, anchored patterns:
var adminPathPattern = regexp.MustCompile(`^/admin(/|$)`)
func adminAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
if adminPathPattern.MatchString(path) {
// Admin authentication logic
if !isAuthenticated(c) {
c.JSON(401, gin.H{"error": "unauthorized"})
c.Abort()
return
}
}
c.Next()
}
}Implement timeout protection for regex operations:
func safeMatch(pattern *regexp.Regexp, input string, timeout time.Duration) (bool, error) {
result := make(chan bool, 1)
var matched bool
var err error
go func() {
matched = pattern.MatchString(input)
result <- matched
}()
select {
case matched = <-result:
return matched, nil
case <-time.After(timeout):
return false, errors.New("regex timeout")
}
}
// Usage in handler
router.GET("/search/:query", func(c *gin.Context) {
query := c.Param("query")
if len(query) > 200 {
c.JSON(400, gin.H{"error": "query too long"})
return
}
matched, err := safeMatch(safePattern, query, 100*time.Millisecond)
if err != nil {
c.JSON(500, gin.H{"error": "processing error"})
return
}
if matched {
c.JSON(200, gin.H{"result": "match"})
} else {
c.JSON(200, gin.H{"result": "no match"})
}
})Finally, integrate middleBrick's continuous monitoring to automatically scan your Gin APIs for regex DoS vulnerabilities. The Pro plan's continuous monitoring will catch new vulnerabilities introduced in code changes before they reach production.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |
Frequently Asked Questions
Why are nested quantifiers in regex patterns dangerous for Gin applications?
(a+)+ or (a*)* create exponential time complexity because the regex engine must explore every possible way to divide the input string among the groups. In Gin's routing layer, this means a single malicious request can cause the router to spend seconds or minutes processing, consuming 100% CPU and blocking other requests. The vulnerability is especially dangerous because Gin processes routes sequentially, so one slow regex can block the entire request processing pipeline.How does middleBrick detect Regex DoS vulnerabilities in Gin applications?
(a+)+b or unanchored nested quantifiers.