Sql Injection in Echo Go
How Sql Injection Manifests in Echo Go
SQL injection in Echo Go applications typically occurs when user input is directly concatenated into SQL queries without proper sanitization. Echo Go's simplicity and rapid development capabilities can inadvertently lead developers to write vulnerable database code, especially when handling HTTP request parameters.
The most common vulnerability pattern in Echo Go involves using echo.Context parameters directly in query construction. Consider this vulnerable pattern:
func getUser(c echo.Context) error {
id := c.Param("id")
query := fmt.Sprintf("SELECT * FROM users WHERE id = %s", id)
row, err := db.Query(query)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
defer row.Close()
var user User
if row.Next() {
err = row.Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
return c.JSON(http.StatusOK, user)
}
return c.JSON(http.StatusNotFound, map[string]string{"error": "User not found"})
}An attacker can exploit this by sending requests like /user/1 OR 1=1, which would return all users in the database. More sophisticated attacks might use /user/1; DROP TABLE users; to delete entire tables.
Echo Go's parameter binding can also introduce vulnerabilities when developers use c.Bind() without validation:
type UserFilter struct {
Role string `json:"role"`
Status string `json:"status"`
}
func getUsers(c echo.Context) error {
var filter UserFilter
if err := c.Bind(&filter); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid input"})
}
query := fmt.Sprintf("SELECT * FROM users WHERE role = '%s' AND status = '%s'", filter.Role, filter.Status)
rows, err := db.Query(query)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
defer rows.Close()
var users []User
for rows.Next() {
var user User
err = rows.Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
}
users = append(users, user)
}
return c.JSON(http.StatusOK, users)
}Without proper input validation, an attacker could send { "role": "admin' OR '1'='1", "status": "anything" } to bypass authentication or access unauthorized data.
Echo Go-Specific Detection
Detecting SQL injection in Echo Go applications requires both static code analysis and dynamic runtime testing. middleBrick's scanner specifically targets Echo Go patterns by examining HTTP endpoints for vulnerable database query construction.
middleBrick identifies SQL injection vulnerabilities by:
- Scanning Echo Go route handlers for direct string concatenation in SQL queries
- Testing parameterized inputs with SQL metacharacters (', ", ;, --, /* */)
- Analyzing the application's response patterns to determine if queries are being executed
- Checking for Echo Go's
c.Param(),c.QueryParam(), andc.Bind()usage without proper sanitization
Here's how middleBrick detects SQL injection in an Echo Go application:
# Install middleBrick CLI
npm install -g middlebrick
# Scan an Echo Go API endpoint
middlebrick scan https://api.example.com/users/123
# Scan with JSON output for integration
middlebrick scan https://api.example.com/users/123 --format=json
The scanner tests for SQL injection by sending payloads like:
# Basic injection test
GET /users/1' OR '1'='1
# Union-based injection
GET /users/1' UNION SELECT username, password FROM users--
# Time-based injection (if database supports it)
GET /users/1' WAITFOR DELAY '0:0:5'--
middleBrick analyzes the HTTP responses to determine if the injection was successful. A successful SQL injection might return different response codes, altered response bodies, or execution time delays that indicate database interaction.
For Echo Go applications using GORM or other ORMs, middleBrick also checks for unsafe raw query usage:
// Vulnerable GORM usage
func getUsersByRole(c echo.Context) error {
role := c.QueryParam("role")
var users []User
// This is vulnerable if role contains malicious input
db.Raw("SELECT * FROM users WHERE role = ?", role).Scan(&users)
return c.JSON(http.StatusOK, users)
}
middleBrick flags this pattern and tests whether the ORM's parameterization is being bypassed or if the query construction is unsafe.
Echo Go-Specific Remediation
Remediating SQL injection in Echo Go requires adopting parameterized queries and input validation. Echo Go's ecosystem provides several approaches to secure database interactions.
The most effective remediation is using prepared statements with proper parameter binding. Here's a secure version of the vulnerable user retrieval function:
func getUser(c echo.Context) error {
id := c.Param("id")
// Validate that id is a valid integer
if _, err := strconv.Atoi(id); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid user ID"})
}
// Use prepared statement with parameter binding
stmt, err := db.Prepare("SELECT id, name, email FROM users WHERE id = ?")
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Database error"})
}
defer stmt.Close()
row := stmt.QueryRow(id)
var user User
if err := row.Scan(&user.ID, &user.Name, &user.Email); err != nil {
if err == sql.ErrNoRows {
return c.JSON(http.StatusNotFound, map[string]string{"error": "User not found"})
}
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Database error"})
}
return c.JSON(http.StatusOK, user)
}For Echo Go applications using GORM, the secure pattern is:
func getUsersByRole(c echo.Context) error {
role := c.QueryParam("role")
// Validate role against allowed values
allowedRoles := map[string]bool{"admin": true, "user": true, "guest": true}
if !allowedRoles[role] {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid role"})
}
var users []User
// Use GORM's safe query builder
result := db.Where("role = ?", role).Find(&users)
if result.Error != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Database error"})
}
return c.JSON(http.StatusOK, users)
}Echo Go developers should also implement input validation middleware. Here's a reusable validation middleware:
func validateInput(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Example: validate numeric parameters
if param := c.Param("id"); param != "" {
if _, err := strconv.Atoi(param); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid ID format"})
}
}
// Example: validate query parameters
if role := c.QueryParam("role"); role != "" {
allowedRoles := map[string]bool{"admin": true, "user": true}
if !allowedRoles[role] {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid role"})
}
}
return next(c)
}
}
// Usage in Echo Go application
e := echo.New()
e.Use(validateInput)
For complex applications, consider using Echo Go's validation tags with struct binding:
type UserFilter struct {
Role string `json:"role" validate:"oneof=admin user guest"`
Status string `json:"status" validate:"oneof=active inactive"`
}
func getUsers(c echo.Context) error {
var filter UserFilter
if err := c.Bind(&filter); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid input format"})
}
// Validate using go-playground/validator
if err := validate.Struct(filter); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
}
// Safe query using GORM
var users []User
db.Where("role = ? AND status = ?", filter.Role, filter.Status).Find(&users)
return c.JSON(http.StatusOK, users)
}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 |