Security Considerations for Using interpolateParams with Multi-Byte Character Encodings

Enabling interpolateParams with certain multi-byte MySQL collations like GBK or BIG5 creates SQL injection risks due to trailing backslash bytes (0x5c), but the driver blocks unsafe combinations at DSN parse time via a hard-coded deny-list.

The go-sql-driver/mysql driver optimizes query execution by offering interpolateParams, a mode that substitutes ? placeholders with literal values before transmission to eliminate prepared-statement round-trips. When working with multi-byte character encodings such as UTF8MB4, developers must understand the specific security boundaries enforced in collations.go and dsn.go to prevent SQL injection vulnerabilities arising from ambiguous byte sequences.

The SQL Injection Risk of Multi-Byte Trailing Bytes

When interpolateParams is enabled, the driver assumes responsibility for quoting and escaping parameter values that would normally be handled by the MySQL server. The driver builds the final query string by writing literals for simple Go types and enclosing []byte or json.RawMessage values in quotes before applying byte-level escaping.

The critical vulnerability arises with multi-byte character sets like GBK, BIG5, and SJIS, where a valid character’s trailing byte can equal 0x5c—the ASCII backslash character. In connection.go, the driver’s escapeBytesBackslash function (used by default) escapes special characters by prepending backslashes. If a multi-byte character’s trailing byte is a backslash, the escaping logic could misinterpret it as an escape sequence rather than part of a character, breaking the quoting boundary and allowing attacker-controlled payloads to bypass sanitization.

Built-In Protection: The unsafeCollations Deny-List

To mitigate this vector, the driver maintains a static deny-list of unsafe collations in collations.go (lines 251–260). This map explicitly flags encodings known to produce 0x5c trailing bytes:

// collations.go – unsafeCollations map
var unsafeCollations = map[string]bool{
    "big5_chinese_ci": true,
    "sjis_japanese_ci": true,
    "gbk_chinese_ci": true,
    // … (other unsafe collations) …
}

During DSN parsing, the Config.normalize method in dsn.go validates the requested collation against this list when interpolateParams is enabled:

// dsn.go – part of Config.normalize
if cfg.InterpolateParams && cfg.Collation != "" && unsafeCollations[cfg.Collation] {
    return errInvalidDSNUnsafeCollation
}

If an unsafe collation is detected, the driver aborts initialization with errInvalidDSNUnsafeCollation, preemptively blocking the connection before any query interpolation occurs.

How Client-Side Escaping Works for Safe Collations

For permitted collations such as utf8mb4_general_ci, the interpolateParams implementation in connection.go (lines 48–88) handles parameter substitution. The driver selects an escaping strategy based on the server’s statusNoBackslashEscapes flag:

  • Standard mode: Uses escapeBytesBackslash to prefix special characters with backslashes.
  • No-backslash mode: When the server reports statusNoBackslashEscapes, the driver switches to escapeBytesQuotes, escaping single quotes by doubling them rather than using backslashes.

This adaptive logic prevents double-escaping errors, but the underlying safety guarantee remains the collation validation performed during DSN setup.

Configuration Examples: Safe vs. Unsafe Usage

The following examples demonstrate correct and incorrect configurations according to the driver’s source code validation.

Safe usage with UTF8MB4 (permitted)

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // DSN enables interpolation and uses a safe collation
    dsn := "user:pass@tcp(localhost:3306)/testdb?interpolateParams=true&collation=utf8mb4_general_ci"
    db, err := sql.Open("mysql", dsn)
    if err != nil {
        panic(err)
    }
    // The driver will replace the placeholder safely
    rows, err := db.Query("SELECT * FROM users WHERE id = ?", 42)
    _ = rows
}

Because utf8mb4_general_ci is absent from unsafeCollations, Config.normalize permits the connection and interpolateParams executes using the escaping logic defined in connection.go.

Unsafe collation triggers fatal error

dsn := "user:pass@tcp(localhost:3306)/testdb?interpolateParams=true&collation=gbk_chinese_ci"
db, err := sql.Open("mysql", dsn)
if err != nil {
    // err == errInvalidDSNUnsafeCollation
    fmt.Println("Interpolation disabled due to unsafe collation")
}

Here, gbk_chinese_ci matches an entry in the unsafeCollations map, causing dsn.go to return errInvalidDSNUnsafeCollation and prevent the connection from opening.

Summary

  • The driver maintains an unsafeCollations deny-list in collations.go (lines 251–260) that blocks GBK, BIG5, SJIS, and other encodings producing ambiguous 0x5c trailing bytes.
  • When interpolateParams=true is set with an unsafe collation, Config.normalize in dsn.go returns errInvalidDSNUnsafeCollation and refuses the connection.
  • For safe collations like utf8mb4_general_ci, the driver escapes parameters using either escapeBytesBackslash or escapeBytesQuotes (based on the server's statusNoBackslashEscapes flag) as implemented in connection.go.
  • Never bypass the collation validation by modifying driver source; instead, use UTF8MB4 variants or disable interpolateParams when working with legacy multi-byte encodings.

Frequently Asked Questions

Can I use interpolateParams with UTF8MB4 safely?

Yes. UTF8MB4 collations such as utf8mb4_general_ci and utf8mb4_unicode_ci are not present in the unsafeCollations map. According to the go-sql-driver/mysql source code, the driver permits interpolation with these encodings because they do not produce ambiguous trailing backslash bytes that could break the escapeBytesBackslash logic.

What happens if I try to use GBK with interpolateParams enabled?

The DSN parser in dsn.go detects the conflict during Config.normalize and returns errInvalidDSNUnsafeCollation. The connection fails to open, preventing potential injection vectors that could arise from misinterpreted escape sequences when the driver processes byte slices containing multi-byte characters.

Does the server-side NO_BACKSLASH_ESCAPES flag affect security?

The driver adapts its escaping strategy in connection.go by switching from escapeBytesBackslash to escapeBytesQuotes when the server reports statusNoBackslashEscapes. While this prevents double-escaping, it does not override the collation safety check; unsafe collations remain blocked regardless of server flags because the vulnerability stems from byte interpretation, not escape method.

Is client-side interpolation faster than server-side prepared statements?

Yes, interpolateParams eliminates a network round-trip for the prepare and execute phases. However, this performance gain is only safe when the driver validates the collation against the unsafeCollations deny-list in collations.go. Attempting to force interpolation with blocked encodings removes the server’s safeguards without providing the driver’s compensating controls, exposing the application to SQL injection.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →