Go Against Hackers: Protecting Against SQL Injections and XSS in Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Even Go Developers Fall for Hackers' Tricks?

\n\n

Go is renowned for its speed, simplicity, and built-in security tools. But reassuring yourself with the phrase "I write in Go, so I'm safe" is a dangerous illusion. SQL injections and XSS attacks are not relics of the past. They still bring down websites and leak databases daily, and Go is no exception.

\n\n

The problem is that Go gives developers powerful tools but doesn't use them for you. You might accidentally concatenate an SQL query using fmt.Sprintf or output a user comment without escaping — and an attacker will gain access to your database or steal visitors' cookies.

\n\n

In this article, we'll break down the two most dangerous types of attacks — SQL injections and Cross-Site Scripting (XSS) — and show how to prevent them using native Go and popular libraries. You'll get not just theory, but ready-made code templates you can insert into your project today.

\n\n

SQL Injections in Go: How They Occur and How to Nip Them in the Bud

\n\n

Typical Mistake: String Concatenation

\n\n

Imagine you have an endpoint for retrieving a user by ID. The most naive (and dangerous) way to write the query looks like this:

\n\n
package main\n\nimport (\n    "database/sql"\n    "fmt"\n    "log"\n    _ "github.com/lib/pq"\n)\n\nfunc getUserByID(db *sql.DB, userID string) {\n    // DANGEROUS! String concatenation\n    query := fmt.Sprintf("SELECT id, name, email FROM users WHERE id = '%s'", userID)\n    rows, err := db.Query(query)\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer rows.Close()\n    // ... processing\n}
\n\n

If an attacker passes the string 1' OR '1'='1 into userID, the query becomes:

\n\n
SELECT id, name, email FROM users WHERE id = '1' OR '1'='1'
\n\n

Voilà — they get all users from the database. And if they enter 1'; DROP TABLE users; --, you lose the table.

\n\n

Solution: Parameterized Queries (Prepared Statements)

\n\n

The Go driver for PostgreSQL, MySQL, and SQLite supports prepared statements. They separate SQL code from data, and the driver automatically escapes dangerous characters.

\n\n
func getUserByIDSafe(db *sql.DB, userID string) {\n    // SAFE: parameterized query\n    query := "SELECT id, name, email FROM users WHERE id = $1"\n    rows, err := db.Query(query, userID)\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer rows.Close()\n    // ... processing\n}
\n\n

Notice the $1 — this is a placeholder for the first argument. MySQL uses the ? symbol. Now even if userID contains 1' OR '1'='1, the driver treats it as a string value, not as part of the SQL code.

\n\n

Working with the sqlx Package

\n\n

The github.com/jmoiron/sqlx library makes the code even cleaner. It supports named parameters and automatic scanning into structs:

\n\n
import "github.com/jmoiron/sqlx"\n\ntype User struct {\n    ID    int    `db:"id"`\n    Name  string `db:"name"`\n    Email string `db:"email"`\n}\n\nfunc getUserByIDSqlx(db *sqlx.DB, userID string) (*User, error) {\n    user := &User{}\n    // Named parameter :id\n    err := db.Get(user, "SELECT * FROM users WHERE id = :id", sqlx.Named("id", userID))\n    if err != nil {\n        return nil, err\n    }\n    return user, nil\n}
\n\n

Important: Even if you use an ORM like GORM or Ent, make sure it is configured to use prepared statements. Some older versions of ORMs allowed "raw" queries.

\n\n

XSS in Go: When Your Site Shoots Itself in the Foot

\n\n

How an XSS Attack Occurs

\n\n

XSS (Cross-Site Scripting) occurs when you insert user content onto a page without escaping. An ev

Blogs

Book Recommendations