@@ -3,12 +3,14 @@ package handler
33import (
44 "fmt"
55 "strings"
6+ "gpt-load/internal/encryption"
67 app_errors "gpt-load/internal/errors"
78 "gpt-load/internal/models"
89 "gpt-load/internal/response"
910 "time"
1011
1112 "github.com/gin-gonic/gin"
13+ "github.com/sirupsen/logrus"
1214)
1315
1416// Stats Get dashboard statistics
@@ -261,6 +263,38 @@ func (s *Server) getSecurityWarnings() []models.SecurityWarning {
261263 warnings = append (warnings , encryptionWarnings ... )
262264 }
263265
266+ // 检查系统级代理密钥
267+ systemSettings := s .SettingsManager .GetSettings ()
268+ if systemSettings .ProxyKeys != "" {
269+ proxyKeys := strings .Split (systemSettings .ProxyKeys , "," )
270+ for i , key := range proxyKeys {
271+ key = strings .TrimSpace (key )
272+ if key != "" {
273+ keyName := fmt .Sprintf ("全局代理密钥 #%d" , i + 1 )
274+ proxyWarnings := checkPasswordSecurity (key , keyName )
275+ warnings = append (warnings , proxyWarnings ... )
276+ }
277+ }
278+ }
279+
280+ // 检查分组级代理密钥
281+ var groups []models.Group
282+ if err := s .DB .Where ("proxy_keys IS NOT NULL AND proxy_keys != ''" ).Find (& groups ).Error ; err == nil {
283+ for _ , group := range groups {
284+ if group .ProxyKeys != "" {
285+ proxyKeys := strings .Split (group .ProxyKeys , "," )
286+ for i , key := range proxyKeys {
287+ key = strings .TrimSpace (key )
288+ if key != "" {
289+ keyName := fmt .Sprintf ("分组 [%s] 的代理密钥 #%d" , group .Name , i + 1 )
290+ proxyWarnings := checkPasswordSecurity (key , keyName )
291+ warnings = append (warnings , proxyWarnings ... )
292+ }
293+ }
294+ }
295+ }
296+ }
297+
264298 return warnings
265299}
266300
@@ -344,3 +378,92 @@ func hasGoodComplexity(password string) bool {
344378
345379 return count >= 3
346380}
381+
382+ // EncryptionStatus checks if ENCRYPTION_KEY is configured but keys are not encrypted
383+ func (s * Server ) EncryptionStatus (c * gin.Context ) {
384+ hasMismatch , message , suggestion := s .checkEncryptionMismatch ()
385+
386+ response .Success (c , gin.H {
387+ "has_mismatch" : hasMismatch ,
388+ "message" : message ,
389+ "suggestion" : suggestion ,
390+ })
391+ }
392+
393+ // checkEncryptionMismatch detects encryption configuration mismatches
394+ func (s * Server ) checkEncryptionMismatch () (bool , string , string ) {
395+ encryptionKey := s .config .GetEncryptionKey ()
396+
397+ // Sample check API keys
398+ var sampleKeys []models.APIKey
399+ if err := s .DB .Limit (20 ).Where ("key_hash IS NOT NULL AND key_hash != ''" ).Find (& sampleKeys ).Error ; err != nil {
400+ logrus .WithError (err ).Error ("Failed to fetch sample keys for encryption check" )
401+ return false , "" , ""
402+ }
403+
404+ if len (sampleKeys ) == 0 {
405+ // No keys in database, no mismatch
406+ return false , "" , ""
407+ }
408+
409+ // Check hash consistency with unencrypted data
410+ noopService , err := encryption .NewService ("" )
411+ if err != nil {
412+ logrus .WithError (err ).Error ("Failed to create noop encryption service" )
413+ return false , "" , ""
414+ }
415+
416+ unencryptedHashMatchCount := 0
417+ for _ , key := range sampleKeys {
418+ // For unencrypted data: key_hash should match SHA256(key_value)
419+ expectedHash := noopService .Hash (key .KeyValue )
420+ if expectedHash == key .KeyHash {
421+ unencryptedHashMatchCount ++
422+ }
423+ }
424+
425+ unencryptedConsistencyRate := float64 (unencryptedHashMatchCount ) / float64 (len (sampleKeys ))
426+
427+ // If ENCRYPTION_KEY is configured, also check if current key can decrypt the data
428+ var currentKeyHashMatchCount int
429+ if encryptionKey != "" {
430+ currentService , err := encryption .NewService (encryptionKey )
431+ if err == nil {
432+ for _ , key := range sampleKeys {
433+ // Try to decrypt and re-hash to check if current key matches
434+ decrypted , err := currentService .Decrypt (key .KeyValue )
435+ if err == nil {
436+ // Successfully decrypted, check if hash matches
437+ expectedHash := currentService .Hash (decrypted )
438+ if expectedHash == key .KeyHash {
439+ currentKeyHashMatchCount ++
440+ }
441+ }
442+ }
443+ }
444+ }
445+ currentKeyConsistencyRate := float64 (currentKeyHashMatchCount ) / float64 (len (sampleKeys ))
446+
447+ // Scenario A: ENCRYPTION_KEY configured but data not encrypted
448+ if encryptionKey != "" && unencryptedConsistencyRate > 0.8 {
449+ return true ,
450+ "检测到您已配置 ENCRYPTION_KEY,但数据库中的密钥尚未加密。这会导致密钥无法正常读取(显示为 failed-to-decrypt)。" ,
451+ "请停止服务,执行密钥迁移命令后重启"
452+ }
453+
454+ // Scenario B: ENCRYPTION_KEY not configured but data is encrypted
455+ if encryptionKey == "" && unencryptedConsistencyRate < 0.2 {
456+ return true ,
457+ "检测到数据库中的密钥已加密,但未配置 ENCRYPTION_KEY。这会导致密钥无法正常读取。" ,
458+ "请配置与加密时相同的 ENCRYPTION_KEY,或执行解密迁移"
459+ }
460+
461+ // Scenario C: ENCRYPTION_KEY configured but doesn't match encrypted data
462+ if encryptionKey != "" && unencryptedConsistencyRate < 0.2 && currentKeyConsistencyRate < 0.2 {
463+ return true ,
464+ "检测到您配置的 ENCRYPTION_KEY 与数据加密时使用的密钥不匹配。这会导致密钥解密失败(显示为 failed-to-decrypt)。" ,
465+ "请使用正确的 ENCRYPTION_KEY,或执行密钥迁移"
466+ }
467+
468+ return false , "" , ""
469+ }
0 commit comments