security: 会话可撤销与认证面加固
- users 加 token_version(schema v4):改密/登出递增,该用户全部已发 access/refresh 立即作废;refresh 改轮换式换发新 cookie - 邀请码改精确匹配 + 注册成功后消费(一码一用),修复子串匹配让任意后缀放行、可无限薅体验金 - 生产校验拦下 .env.example 占位密钥;OT_ENV 枚举校验(拼错不再静默跳过断言);生产强制 COOKIE_SECURE;非 development 拒绝默认密码播种管理员 - WebAuthn 会话启用过期校验 + 4096 容量上限(未认证 login/begin 可无限撑爆内存),begin/complete 挂登录级限速 - /api/v1 挂 1MB 请求体上限;login 先限 IP 桶再解析 body;全局安全响应头 + CSP - 连接池配置(PG 30/SQLite 8);SQLite DSN 自动追加 WAL + busy_timeout(驱动只认 _pragma,旧注释的 _journal_mode 写法不生效)
This commit is contained in:
1 parent
d6db0f8162
commit
e3ff0d3329
14 files changed
+392
-37
No files matched your search
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
@@ -20,16 +21,18 @@ import (
|
||||
// 注意:c39c3e7 给 Model 加了 cache_write_price/context_length/supports_tools/
|
||||
// supports_vision/supports_reasoning 等列并删掉了 display_name,但当时漏改这里,
|
||||
// 导致已建库的实例永远跳过重迁移、新增模型时 Create 因缺列而失败。这里补到 3 触发一次迁移。
|
||||
const schemaVersion = 3
|
||||
// v4:users 加 token_version(会话撤销)。
|
||||
const schemaVersion = 4
|
||||
|
||||
// Open 打开数据库连接并自动迁移。
|
||||
// 开发默认 SQLite(dsn 支持 file:...?_journal_mode=WAL),生产可切 postgres。
|
||||
// 开发默认 SQLite(自动追加 WAL 与 busy_timeout,见 withSQLitePragmas),生产可切 postgres。
|
||||
func Open(driver, dsn string) (*gorm.DB, error) {
|
||||
var dialector gorm.Dialector
|
||||
switch driver {
|
||||
case "postgres":
|
||||
dialector = postgresDialector(dsn)
|
||||
default:
|
||||
dsn = withSQLitePragmas(dsn)
|
||||
// 确保 SQLite 文件所在目录存在
|
||||
if dir := sqliteDir(dsn); dir != "" {
|
||||
_ = os.MkdirAll(dir, 0o755)
|
||||
@@ -43,6 +46,7 @@ func Open(driver, dsn string) (*gorm.DB, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configurePool(db, driver)
|
||||
|
||||
if driver != "postgres" && currentSQLiteVersion(db) >= schemaVersion {
|
||||
log.Printf("store: connected driver=%s (schema up-to-date v%d, skip migrate)", driver, schemaVersion)
|
||||
@@ -61,6 +65,43 @@ func Open(driver, dsn string) (*gorm.DB, error) {
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// withSQLitePragmas 给 DSN 补上 busy_timeout 与 WAL。
|
||||
// glebarez(modernc)驱动只解析 _pragma/_txlock/_time_format/vfs 四种参数——
|
||||
// 旧注释宣称的 `?_journal_mode=WAL` 实际不生效,缺 WAL 时并发写会频繁 database is locked。
|
||||
// 用户 DSN 里已显式声明的项不覆盖。
|
||||
func withSQLitePragmas(dsn string) string {
|
||||
for _, p := range []string{"_pragma=busy_timeout(10000)", "_pragma=journal_mode(WAL)"} {
|
||||
if strings.Contains(dsn, p) {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(dsn, "?") {
|
||||
dsn += "&" + p
|
||||
} else {
|
||||
dsn += "?" + p
|
||||
}
|
||||
}
|
||||
return dsn
|
||||
}
|
||||
|
||||
// configurePool 连接池上限。此前全仓无任何配置:PG 连接数无上限,
|
||||
// SQLite 多连接并发写叠加无 WAL 加剧锁竞争。
|
||||
func configurePool(db *gorm.DB, driver string) {
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if driver == "postgres" {
|
||||
sqlDB.SetMaxOpenConns(30)
|
||||
sqlDB.SetMaxIdleConns(10)
|
||||
sqlDB.SetConnMaxLifetime(30 * time.Minute)
|
||||
sqlDB.SetConnMaxIdleTime(5 * time.Minute)
|
||||
return
|
||||
}
|
||||
// SQLite:WAL 下读写可并发,写仍串行——限连接数让写竞争在 busy_timeout 内排队
|
||||
sqlDB.SetMaxOpenConns(8)
|
||||
sqlDB.SetConnMaxIdleTime(10 * time.Minute)
|
||||
}
|
||||
|
||||
// currentSQLiteVersion 读取 PRAGMA user_version。
|
||||
func currentSQLiteVersion(db *gorm.DB) int {
|
||||
var v int
|
||||
|
||||
@@ -56,6 +56,7 @@ type User struct {
|
||||
Role string `gorm:"size:16;not null;default:user" json:"role"`
|
||||
Balance float64 `gorm:"type:numeric(20,8);not null;default:0" json:"balance"`
|
||||
Status string `gorm:"size:16;not null;default:active" json:"status"`
|
||||
TokenVersion int `gorm:"not null;default:0" json:"-"` // 会话版本:改密/登出递增,低于当前版本的 token 全部失效
|
||||
AllowedModels []string `gorm:"type:jsonb;serializer:json" json:"allowed_models,omitempty"` // 用户级模型白名单(空=不限制)
|
||||
DeniedModels []string `gorm:"type:jsonb;serializer:json" json:"denied_models,omitempty"` // 用户级模型黑名单
|
||||
InviteCode *string `json:"invite_code,omitempty"`
|
||||
|
||||
Reference in new issue
Block a user