fix(supply-api): 修复P2-05数据库凭证日志泄露风险
1. 在DatabaseConfig中添加SafeDSN()方法,返回脱敏的连接信息 2. 在NewDB中使用SafeDSN()记录日志 3. 添加sanitizeErrorPassword()函数清理错误信息中的密码 修复的问题:P2-05 数据库凭证日志泄露风险
This commit is contained in:
@@ -66,12 +66,19 @@ type AuditConfig struct {
|
||||
ExportTimeout time.Duration
|
||||
}
|
||||
|
||||
// DSN 返回数据库连接字符串
|
||||
// DSN 返回数据库连接字符串(包含明文密码,仅限内部使用)
|
||||
func (d *DatabaseConfig) DSN() string {
|
||||
return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable",
|
||||
d.User, d.Password, d.Host, d.Port, d.Database)
|
||||
}
|
||||
|
||||
// SafeDSN 返回脱敏的数据库连接字符串(密码被替换为***),用于日志记录
|
||||
// P2-05: 避免在日志中泄露数据库密码
|
||||
func (d *DatabaseConfig) SafeDSN() string {
|
||||
return fmt.Sprintf("postgres://%s:***@%s:%d/%s?sslmode=disable",
|
||||
d.User, d.Host, d.Port, d.Database)
|
||||
}
|
||||
|
||||
// Addr 返回Redis地址
|
||||
func (r *RedisConfig) Addr() string {
|
||||
return fmt.Sprintf("%s:%d", r.Host, r.Port)
|
||||
|
||||
@@ -3,6 +3,7 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
@@ -17,9 +18,11 @@ type DB struct {
|
||||
|
||||
// NewDB 创建数据库连接池
|
||||
func NewDB(ctx context.Context, cfg config.DatabaseConfig) (*DB, error) {
|
||||
poolConfig, err := pgxpool.ParseConfig(cfg.DSN())
|
||||
dsn := cfg.DSN()
|
||||
poolConfig, err := pgxpool.ParseConfig(dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse database config: %w", err)
|
||||
// P2-05: 使用SafeDSN替代DSN,避免在错误信息中泄露密码
|
||||
return nil, fmt.Errorf("failed to parse database config for %s: %v", cfg.SafeDSN(), sanitizeErrorPassword(err, cfg.Password))
|
||||
}
|
||||
|
||||
poolConfig.MaxConns = int32(cfg.MaxOpenConns)
|
||||
@@ -30,18 +33,34 @@ func NewDB(ctx context.Context, cfg config.DatabaseConfig) (*DB, error) {
|
||||
|
||||
pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create connection pool: %w", err)
|
||||
// P2-05: 清理错误信息中的密码
|
||||
return nil, fmt.Errorf("failed to create connection pool for %s: %v", cfg.SafeDSN(), sanitizeErrorPassword(err, cfg.Password))
|
||||
}
|
||||
|
||||
// 验证连接
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
pool.Close()
|
||||
return nil, fmt.Errorf("failed to ping database: %w", err)
|
||||
return nil, fmt.Errorf("failed to ping database at %s:%d: %v", cfg.Host, cfg.Port, err)
|
||||
}
|
||||
|
||||
return &DB{Pool: pool}, nil
|
||||
}
|
||||
|
||||
// sanitizeErrorPassword 从错误信息中清理密码
|
||||
// P2-05: pgxpool.ParseConfig的错误信息可能包含完整的DSN,需要清理
|
||||
func sanitizeErrorPassword(err error, password string) error {
|
||||
if err == nil || password == "" {
|
||||
return err
|
||||
}
|
||||
// 将错误信息中的密码替换为***
|
||||
errStr := err.Error()
|
||||
safeErrStr := strings.ReplaceAll(errStr, password, "***")
|
||||
if safeErrStr != errStr {
|
||||
return fmt.Errorf("%s (password sanitized)", safeErrStr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Close 关闭连接池
|
||||
func (db *DB) Close() {
|
||||
if db.Pool != nil {
|
||||
|
||||
Reference in New Issue
Block a user