Prevention of SQL Injection in headers and application fields
package main
import (
"github.com/gin-gonic/gin"
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err.Error())
}
defer db.Close()
r := gin.Default()
r.GET("/client", func(c *gin.Context) {
idClient := c.GetHeader("idClient")
var result string
err := db.QueryRow(fmt.Sprintf("SELECT name FROM clients WHERE id = '%s'", idClient)).Scan(&result)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"client_name": result})
})
r.Run()
}
The vulnerable code is a simple Go application using the Gin framework and the go-sql-driver/mysql driver to interact with a MySQL database.
The vulnerability lies in the
/client
route handler function. The application retrieves the
idClient
header from the incoming request and directly uses it to construct a SQL query. This is a classic example of unparameterized SQL, which opens the door to SQL Injection attacks.
idClient := c.GetHeader("idClient")
var result string
err := db.QueryRow(fmt.Sprintf("SELECT name FROM clients WHERE id = '%s'", idClient)).Scan(&result)
idClient
header value like
1'; DROP TABLE clients; --
, which would result in the following SQL statement:
SELECT name FROM clients WHERE id = '1'; DROP TABLE clients; --'
. This would delete the entire
clients
table from the database.
package main
import (
"github.com/gin-gonic/gin"
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err.Error())
}
defer db.Close()
r := gin.Default()
r.GET("/client", func(c *gin.Context) {
idClient := c.GetHeader("idClient")
stmt, err := db.Prepare("SELECT name FROM clients WHERE id = ?")
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
defer stmt.Close()
var result string
err = stmt.QueryRow(idClient).Scan(&result)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"client_name": result})
})
r.Run()
}
The original code was vulnerable to SQL Injection through the
idClient
header. This was because the
idClient
header was directly concatenated into the SQL query, which allows an attacker to manipulate the SQL query by injecting SQL statements into the
idClient
header.
The fixed code uses parameterized queries to prevent SQL Injection. Instead of concatenating the
idClient
header directly into the SQL query, a placeholder (
?
) is used. The actual
idClient
value is passed as a parameter to the
QueryRow
method of the prepared statement. This ensures that the
idClient
value is properly escaped, preventing SQL Injection.
In addition, the fixed code also adds error handling for the preparation of the SQL statement. If there is an error while preparing the SQL statement (for example, if the SQL syntax is incorrect), the error will be returned as a JSON response with a 500 status code. This helps to prevent potential SQL Injection attempts from causing unhandled errors in the application.