Format String with Bearer Tokens
How Format String Manifests in Bearer Tokens
Format string vulnerabilities in Bearer Token implementations occur when token validation logic uses unsafe string formatting functions. These vulnerabilities allow attackers to manipulate token parsing logic and potentially bypass authentication.
The most common manifestation appears in logging and error message generation. Consider a typical Bearer Token validation function that logs authentication failures:
func validateBearerToken(token string) error {
if len(token) == 0 {
log.Printf("Bearer token validation failed: %s", token)
return errors.New("missing token")
}
// validation logic...
return nil
}
The critical vulnerability lies in the log.Printf call. If an attacker supplies a token containing format specifiers like %x or %n, they can manipulate the logging output and potentially read memory contents or write to arbitrary memory locations.
Another common pattern involves error message construction:
func parseBearerToken(header string) (string, error) {
if !strings.HasPrefix(header, "Bearer ") {
return "", fmt.Errorf("invalid bearer token format: %s", header)
}
return header[7:], nil
}
Here, an attacker could craft a header like Bearer %08x.%08x.%08x to cause the error message to print memory contents instead of the actual header value.
Format string vulnerabilities also appear in token introspection endpoints. Many implementations construct introspection responses using string formatting:
func introspectToken(token string) (map[string]interface{}, error) {
result := make(map[string]interface{})
// Simulated introspection logic
if isValid(token) {
result["active"] = true
result["scope"] = "read write"
result["message"] = fmt.Sprintf("Token %s is valid", token)
} else {
result["active"] = false
result["message"] = fmt.Sprintf("Token validation failed: %s", token)
}
return result, nil
}
This pattern is particularly dangerous because introspection endpoints often return detailed information about token validity, making them attractive targets for attackers.
Database logging represents another attack surface. Consider a logging system that records authentication attempts:
func logAuthAttempt(token string, success bool) {
query := fmt.Sprintf("INSERT INTO auth_logs (token, success) VALUES ('%s', %t)", token, success)
db.Exec(query)
}
While this appears to be SQL injection territory, format string vulnerabilities can still occur if the logging system processes these strings through additional formatting layers or if the database driver performs unsafe string operations.
Bearer Tokens-Specific Detection
Detecting format string vulnerabilities in Bearer Token implementations requires both static analysis and dynamic testing approaches. The most effective detection combines code review with automated scanning.
Static analysis should focus on identifying unsafe string formatting patterns:
grep -r "fmt\.Printf\|fmt\.Sprintf\|fmt\.Errorf" --include="*.go" |
while read -r line; do
file=$(echo "$line" | cut -d: -f1)
line_num=$(echo "$line" | cut -d: -f2)
content=$(sed -n "${line_num}p" "$file")
if [[ "$content" =~ (fmt\.Printf|fmt\.Sprintf|fmt\.Errorf).*"%[a-zA-Z]" ]]; then
echo "Potential format string: $file:$line_num: $content"
fi
done
This script identifies lines using format strings where the format argument might come from untrusted sources like tokens or headers.
Dynamic testing involves crafting malicious tokens and observing system behavior. A comprehensive test suite should include:
package main
import (
"fmt"
"testing"
)
func TestFormatStringVulnerabilities(t *testing.T) {
testCases := []string{
"%s", // basic string format
"%x", // hex format
"%p", // pointer format
"%n", // write integer format
"%08x", // padded hex
"%s.%s.%s", // multiple format specifiers
}
for _, tc := range testCases {
// Test logging
result := captureLog(func() {
logBearerToken(tc)
})
if containsMemoryContent(result) {
t.Errorf("Format string vulnerability detected in logging: %s", tc)
}
// Test error messages
_, err := validateBearerToken(tc)
if err != nil && containsMemoryContent(err.Error()) {
t.Errorf("Format string vulnerability in error message: %s", tc)
}
}
}
Automated scanning tools like middleBrick can detect these vulnerabilities by analyzing the unauthenticated attack surface. The scanner examines how Bearer Token endpoints handle unexpected format specifiers and identifies unsafe string formatting patterns in the authentication flow.
middleBrick specifically tests for format string vulnerabilities by submitting tokens containing format specifiers and analyzing the responses for memory disclosure or abnormal behavior. The scanner checks:
- Log output for memory content disclosure
- Error messages for format string exploitation
- API responses for unexpected data exposure
- Database queries for unsafe string formatting
The scanner's LLM security module also detects if format string vulnerabilities could be exploited to exfiltrate sensitive data through AI-powered endpoints that process authentication tokens.
Bearer Tokens-Specific Remediation
Remediating format string vulnerabilities in Bearer Token implementations requires systematic code review and the application of safe string handling practices. The primary defense is eliminating unsafe string formatting functions.
Replace unsafe formatting with safe alternatives:
// Vulnerable pattern
log.Printf("Bearer token validation failed: %s", token)
// Safe pattern
log.Printf("Bearer token validation failed: %s", html.EscapeString(token))
// Or use structured logging
zap.L().Error("Bearer token validation failed", zap.String("token", token))
Structured logging libraries like Zap, Logrus, or standard library log with proper escaping eliminate format string vulnerabilities by treating all arguments as data rather than format strings.
For error message construction, use explicit string concatenation or safe formatting:
// Vulnerable
return fmt.Errorf("invalid bearer token format: %s", header)
// Safe
return fmt.Errorf("invalid bearer token format: %s", sanitize(header))
func sanitize(input string) string {
return strings.ReplaceAll(input, "%", "%%")
}
The sanitization function escapes percent signs, preventing format specifiers from being interpreted.
Implement input validation at the token parsing stage:
func parseBearerToken(header string) (string, error) {
if !strings.HasPrefix(header, "Bearer ") {
return "", errors.New("invalid bearer token format")
}
token := header[7:]
if containsFormatSpecifiers(token) {
return "", errors.New("token contains invalid characters")
}
return token, nil
}
func containsFormatSpecifiers(s string) bool {
return strings.ContainsAny(s, "%") &&
(strings.Contains(s, "%s") || strings.Contains(s, "%x") ||
strings.Contains(s, "%p") || strings.Contains(s, "%n"))
}
This proactive validation rejects tokens containing format specifiers before they can be used in unsafe operations.
For logging and monitoring systems, implement context-aware logging:
type authAttempt struct {
token string
success bool
ip string
timestamp time.Time
}
func logAuthAttempt(attempt authAttempt) {
// Structured logging - no string formatting
zap.L().Info("auth attempt",
zap.String("token_prefix", attempt.token[:8]),
zap.Bool("success", attempt.success),
zap.String("ip", attempt.ip),
zap.Time("timestamp", attempt.timestamp))
}
Structured logging prevents format string vulnerabilities while providing better observability through queryable log fields.
Implement comprehensive testing with format string payloads:
func TestFormatSafeBearerToken(t *testing.T) {
payloads := []string{
"%s", "%x", "%p", "%n",
"%08x.%08x.%08x",
"Bearer %s",
}
for _, payload := range payloads {
// Test token parsing
_, err := parseBearerToken("Bearer " + payload)
if err == nil {
t.Errorf("Format string payload %q should be rejected", payload)
}
// Test logging safety
captured := captureLog(func() {
logAuthAttempt(authAttempt{token: payload, success: false, ip: "127.0.0.1"})
})
if strings.Contains(captured, "panic") || containsMemoryContent(captured) {
t.Errorf("Logging vulnerable to format string: %q", payload)
}
}
}
Regular security scanning with tools like middleBrick should be integrated into the development workflow to catch format string vulnerabilities before deployment. The scanner's continuous monitoring can alert teams when new vulnerabilities are introduced.