Implementation of proper input validation and use of parameterized statements or stored procedures to prevent SQL injection attacks.
package main
import (
"database/sql"
"fmt"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err)
}
defer db.Close()
r := gin.Default()
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
rows, err := db.Query(fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name))
if err != nil {
panic(err)
}
defer rows.Close()
var result []string
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
panic(err)
}
result = append(result, name)
}
c.JSON(200, gin.H{
"users": result,
})
})
r.Run()
}
The above code is a simple Go backend application using the Gin framework and interacting with a MySQL database. The application has a single endpoint
/user/:name
that fetches a user from the database by their name.
The vulnerability lies in the way SQL query is constructed in the handler function for the endpoint. The user-provided name is directly embedded into the SQL query string without any validation or parameterization:
rows, err := db.Query(fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name))
' OR '1'='1
which would return all users, not just the one specified by the name.
package main
import (
"database/sql"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err)
}
defer db.Close()
r := gin.Default()
r.GET("/user/:name", func(c *gin.Context) {
name := c.Param("name")
stmt, err := db.Prepare("SELECT * FROM users WHERE name = ?")
if err != nil {
panic(err)
}
defer stmt.Close()
rows, err := stmt.Query(name)
if err != nil {
panic(err)
}
defer rows.Close()
var result []string
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
panic(err)
}
result = append(result, name)
}
c.JSON(200, gin.H{
"users": result,
})
})
r.Run()
}
The original code was vulnerable to SQL Injection attacks because it was dynamically constructing SQL queries using user input. This is a common vulnerability that allows an attacker to manipulate the SQL query to gain unauthorized access to the database, modify the database structure, or access sensitive information.
The fixed code uses parameterized queries, also known as prepared statements, to prevent SQL Injection attacks. In this approach, SQL code and data are sent to the SQL server separately, and the SQL server ensures that the data cannot be treated as SQL code. This effectively eliminates the possibility of SQL Injection attacks.
In the fixed code, the
db.Prepare
function is used to create a prepared statement. The
?
placeholder is used in the SQL query to indicate where the user input should be inserted. The
stmt.Query
function is then used to execute the prepared statement with the user input as a parameter. This ensures that the user input is properly escaped and cannot be used to manipulate the SQL query.
In addition to using prepared statements, it's also important to validate and sanitize user input, implement access controls, regularly update and patch the database software, use a web application firewall, educate developers about secure coding practices, and perform regular security audits and penetration testing.