feat(logging): centralize sensitive header masking

This commit is contained in:
hkfires
2025-10-18 17:16:00 +08:00
parent 307ae76ed4
commit 9f45806106
3 changed files with 52 additions and 17 deletions

View File

@@ -16,6 +16,7 @@ import (
"time" "time"
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces" "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
) )
// RequestLogger defines the interface for logging HTTP requests and responses. // RequestLogger defines the interface for logging HTTP requests and responses.
@@ -485,7 +486,8 @@ func (l *FileRequestLogger) formatRequestInfo(url, method string, headers map[st
content.WriteString("=== HEADERS ===\n") content.WriteString("=== HEADERS ===\n")
for key, values := range headers { for key, values := range headers {
for _, value := range values { for _, value := range values {
content.WriteString(fmt.Sprintf("%s: %s\n", key, value)) masked := util.MaskSensitiveHeaderValue(key, value)
content.WriteString(fmt.Sprintf("%s: %s\n", key, masked))
} }
} }
content.WriteString("\n") content.WriteString("\n")

View File

@@ -275,7 +275,8 @@ func writeHeaders(builder *strings.Builder, headers http.Header) {
continue continue
} }
for _, value := range values { for _, value := range values {
builder.WriteString(fmt.Sprintf("%s: %s\n", key, sanitizeHeaderValue(key, value))) masked := util.MaskSensitiveHeaderValue(key, value)
builder.WriteString(fmt.Sprintf("%s: %s\n", key, masked))
} }
} }
} }
@@ -319,18 +320,3 @@ func formatAuthInfo(info upstreamRequestLog) string {
return strings.Join(parts, ", ") return strings.Join(parts, ", ")
} }
func sanitizeHeaderValue(key, value string) string {
trimmedValue := strings.TrimSpace(value)
lowerKey := strings.ToLower(strings.TrimSpace(key))
switch {
case strings.Contains(lowerKey, "authorization"),
strings.Contains(lowerKey, "api-key"),
strings.Contains(lowerKey, "apikey"),
strings.Contains(lowerKey, "token"),
strings.Contains(lowerKey, "secret"):
return util.HideAPIKey(trimmedValue)
default:
return trimmedValue
}
}

View File

@@ -4,6 +4,8 @@
package util package util
import ( import (
"strings"
"github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry" "github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
) )
@@ -141,3 +143,48 @@ func HideAPIKey(apiKey string) string {
} }
return apiKey return apiKey
} }
// maskAuthorizationHeader masks the Authorization header value while preserving the auth type prefix.
// Common formats: "Bearer <token>", "Basic <credentials>", "ApiKey <key>", etc.
// It preserves the prefix (e.g., "Bearer ") and only masks the token/credential part.
//
// Parameters:
// - value: The Authorization header value
//
// Returns:
// - string: The masked Authorization value with prefix preserved
func MaskAuthorizationHeader(value string) string {
parts := strings.SplitN(strings.TrimSpace(value), " ", 2)
if len(parts) < 2 {
return HideAPIKey(value)
}
return parts[0] + " " + HideAPIKey(parts[1])
}
// MaskSensitiveHeaderValue masks sensitive header values while preserving expected formats.
//
// Behavior by header key (case-insensitive):
// - "Authorization": Preserve the auth type prefix (e.g., "Bearer ") and mask only the credential part.
// - Headers containing "api-key": Mask the entire value using HideAPIKey.
// - Others: Return the original value unchanged.
//
// Parameters:
// - key: The HTTP header name to inspect (case-insensitive matching).
// - value: The header value to mask when sensitive.
//
// Returns:
// - string: The masked value according to the header type; unchanged if not sensitive.
func MaskSensitiveHeaderValue(key, value string) string {
lowerKey := strings.ToLower(strings.TrimSpace(key))
switch {
case lowerKey == "authorization":
return MaskAuthorizationHeader(value)
case strings.Contains(lowerKey, "api-key"),
strings.Contains(lowerKey, "apikey"),
strings.Contains(lowerKey, "token"),
strings.Contains(lowerKey, "secret"):
return HideAPIKey(value)
default:
return value
}
}