Format String Injection
ID |
go.format_string_injection |
Severity |
low |
Resource |
Injection |
Language |
Go |
Tags |
CWE:134, NIST.SP.800-53, PCI-DSS:6.5.1 |
Description
User-controlled input is used as a format string in functions like fmt.Printf
, fmt.Sprintf
, or fmt.Fprintf
, leading to potential injection of unintended format specifiers, which can cause runtime panics or information disclosure.
Rationale
Format string injection occurs when an attacker can control the format string parameter of formatting functions. In Go, functions such as fmt.Printf
, fmt.Sprintf
, fmt.Fprintf
, and others interpret the first argument as a format string. If this argument is derived from user input, the attacker can inject format verbs (like %s
, %x
, %v
) that alter output behavior, leading to:
-
Program crashes due to missing expected arguments.
-
Disclosure of internal memory layout or structure through verbose or hexadecimal format specifiers.
-
Misleading or manipulated output, potentially exploited in logging or error-handling contexts.
This vulnerability is less severe in Go compared to C/C++, as it does not allow direct memory access. However, it is still a reliability and logging integrity risk, especially in APIs and web services that log user-controlled data.
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
msg := r.URL.Query().Get("msg")
fmt.Fprintf(w, msg) // FLAW: attacker controls format string
}
Remediation
Always separate user-controlled data from the format string. The format string should be a constant defined by the developer. Interpolated values should be passed as additional arguments.
Prefer format strings like "%s"
or safer alternatives like fmt.Print
, fmt.Println
, or structured logging where format strings are not dynamically constructed.
Safe examples:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
msg := r.URL.Query().Get("msg")
fmt.Fprintf(w, "%s", msg) // Safe: user input is data, not format
}