Injection

Injection in Go (Gin): Secure Coding Guide [June 2026] [CVE-2026-7731]

[Updated June 2026] Updated CVE-2026-7731

Overview

Injection vulnerabilities in Go applications, including those built with Gin, can allow attackers to alter or access data they should not see. SQL injection can lead to data exfiltration, data manipulation, or bypassing business logic; in some configurations, attackers may gain access to internal resources or perform unauthorized actions if the database user has broad privileges. While Go’s database/sql often prevents these issues by using prepared statements, misusing string concatenation or dynamic SQL in Gin handlers remains a high-risk pattern. In Gin-based handlers, injection typically happens when user-supplied data from query strings, path parameters, or JSON payloads is concatenated into SQL statements or used unsafely in templates or OS commands. The issue is amplified when error details are leaked to the client, or when the app manually builds queries without parameterization. Go's strong typing helps, but it does not automatically protect against injection; developers must explicitly use binding, validation, and parameterized queries. Typical mitigations include using prepared statements or ORM methods that parameterize all inputs, validating and sanitizing input with allowlists, following the principle of least privilege for the database user, and testing with automated injection tests. Ensure error handling does not reveal the exact SQL or schema to clients, and perform code reviews focused on the data access layer.

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
	"database/sql"
	"fmt"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	_ "github.com/mattn/go-sqlite3"
)

var db *sql.DB

func init() {
	var err error
	db, err = sql.Open("sqlite3", ":memory:")
	if err != nil { log.Fatal(err) }
	_, err = db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, name TEXT)")
	if err != nil { log.Fatal(err) }
	_, err = db.Exec("INSERT INTO users (username, name) VALUES ('alice','Alice Example'), ('bob','Bob Builder')")
	if err != nil { log.Fatal(err) }
}

// Vulnerable handler: unsafe string concatenation leading to SQL injection
func vulnerableHandler(c *gin.Context) {
	user := c.Query("user")
	query := "SELECT id, name FROM users WHERE username = '" + user + "'" // vulnerable
	rows, err := db.Query(query)
	if err != nil { c.String(http.StatusInternalServerError, err.Error()); return }
	defer rows.Close()
	var id int
	var name string
	for rows.Next() {
		if err := rows.Scan(&id, &name); err != nil { continue }
		c.String(http.StatusOK, fmt.Sprintf("%d: %s", id, name))
		return
	}
	c.String(http.StatusOK, "no results")
}

// Fixed handler: use parameterized queries to prevent injection
func fixedHandler(c *gin.Context) {
	user := c.Query("user")
	stmt, err := db.Prepare("SELECT id, name FROM users WHERE username = ?")
	if err != nil { c.String(http.StatusInternalServerError, err.Error()); return }
	rows, err := stmt.Query(user)
	if err != nil { c.String(http.StatusInternalServerError, err.Error()); return }
	defer rows.Close()
	var id int
	var name string
	for rows.Next() {
		if err := rows.Scan(&id, &name); err != nil { continue }
		c.String(http.StatusOK, fmt.Sprintf("%d: %s", id, name))
		return
	}
	c.String(http.StatusOK, "no results")
}

func main() {
	r := gin.Default()
	r.GET("/vuln", vulnerableHandler)
	r.GET("/fix", fixedHandler)
	if err := r.Run(":8080"); err != nil { log.Fatal(err) }
}

CVE References

Choose which optional cookies to allow. You can change this any time.