fix: 过滤非法 CORS Origin,避免 Docker 启动 panic
gin-contrib/cors 不接受 tauri://,启动时过滤仅保留 http/https。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
cb1a9be2c4
commit
ac31cc059a
2 files changed
+47
-13
No files matched your search
+30
-12
@@ -48,18 +48,22 @@ func Load() *Config {
|
|||||||
DBPassword: getEnv("DB_PASSWORD", ""),
|
DBPassword: getEnv("DB_PASSWORD", ""),
|
||||||
DBName: getEnv("DB_NAME", "db.sqlite3"),
|
DBName: getEnv("DB_NAME", "db.sqlite3"),
|
||||||
JWTSecret: getEnv("JWT_SECRET", ""),
|
JWTSecret: getEnv("JWT_SECRET", ""),
|
||||||
CORSOrigins: []string{
|
CORSOrigins: defaultCORSOrigins(),
|
||||||
"http://localhost:5181",
|
}
|
||||||
"http://127.0.0.1:5181",
|
|
||||||
"http://localhost:3000",
|
if raw := strings.TrimSpace(os.Getenv("CORS_ORIGINS")); raw != "" {
|
||||||
"http://127.0.0.1:3000",
|
parts := strings.Split(raw, ",")
|
||||||
// OpsXShell Tauri / Vite dev origins
|
origins := make([]string, 0, len(parts))
|
||||||
"http://localhost:1420",
|
for _, p := range parts {
|
||||||
"http://127.0.0.1:1420",
|
p = strings.TrimSpace(p)
|
||||||
"http://tauri.localhost",
|
if p == "" {
|
||||||
"https://tauri.localhost",
|
continue
|
||||||
"tauri://localhost",
|
}
|
||||||
},
|
origins = append(origins, p)
|
||||||
|
}
|
||||||
|
if len(origins) > 0 {
|
||||||
|
cfg.CORSOrigins = origins
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安全检查:生产环境拒绝空密钥,开发环境使用进程级随机密钥避免固定后门。 // Logan.Li
|
// 安全检查:生产环境拒绝空密钥,开发环境使用进程级随机密钥避免固定后门。 // Logan.Li
|
||||||
@@ -74,6 +78,20 @@ func Load() *Config {
|
|||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func defaultCORSOrigins() []string {
|
||||||
|
return []string{
|
||||||
|
"http://localhost:5181",
|
||||||
|
"http://127.0.0.1:5181",
|
||||||
|
"http://localhost:3000",
|
||||||
|
"http://127.0.0.1:3000",
|
||||||
|
// OpsXShell Tauri / Vite dev origins (gin-cors only accepts http/https)
|
||||||
|
"http://localhost:1420",
|
||||||
|
"http://127.0.0.1:1420",
|
||||||
|
"http://tauri.localhost",
|
||||||
|
"https://tauri.localhost",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsOriginAllowed 判断浏览器 Origin 是否在显式 CORS 白名单中。 // Logan.Li
|
// IsOriginAllowed 判断浏览器 Origin 是否在显式 CORS 白名单中。 // Logan.Li
|
||||||
func (c *Config) IsOriginAllowed(origin string) bool {
|
func (c *Config) IsOriginAllowed(origin string) bool {
|
||||||
for _, allowed := range c.CORSOrigins {
|
for _, allowed := range c.CORSOrigins {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"backend/config"
|
"backend/config"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
@@ -10,7 +11,7 @@ import (
|
|||||||
|
|
||||||
// CORSMiddleware CORS 中间件,使用显式白名单避免携带凭据时放行任意来源。 // Logan.Li
|
// CORSMiddleware CORS 中间件,使用显式白名单避免携带凭据时放行任意来源。 // Logan.Li
|
||||||
func CORSMiddleware(cfg *config.Config) gin.HandlerFunc {
|
func CORSMiddleware(cfg *config.Config) gin.HandlerFunc {
|
||||||
origins := cfg.CORSOrigins
|
origins := filterHTTPOrigins(cfg.CORSOrigins)
|
||||||
if len(origins) == 0 {
|
if len(origins) == 0 {
|
||||||
origins = []string{
|
origins = []string{
|
||||||
"http://localhost:5181",
|
"http://localhost:5181",
|
||||||
@@ -29,3 +30,18 @@ func CORSMiddleware(cfg *config.Config) gin.HandlerFunc {
|
|||||||
MaxAge: 12 * time.Hour,
|
MaxAge: 12 * time.Hour,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filterHTTPOrigins keeps only origins gin-contrib/cors accepts (http/https or *). // Logan.Li
|
||||||
|
func filterHTTPOrigins(origins []string) []string {
|
||||||
|
out := make([]string, 0, len(origins))
|
||||||
|
for _, origin := range origins {
|
||||||
|
o := strings.TrimSpace(origin)
|
||||||
|
if o == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if o == "*" || strings.HasPrefix(o, "http://") || strings.HasPrefix(o, "https://") {
|
||||||
|
out = append(out, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
Reference in new issue
Block a user