mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 21:10:51 +08:00
feat(logging): centralize sensitive header masking
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user