security: 每人一把口令(口令即身份) + 随机会话token + 无配置拒绝启动 + 爆破节流
- STORY_WEB_PASSWORD(默认story) 废弃 → STORY_WEB_USERS=名字1:口令1,名字2:口令2; 未配置/口令<8位/口令或用户名重复 → 启动即退出,杜绝弱默认口令裸奔 - cookie 不再存口令原文:登录发 secrets.token_urlsafe(32) 随机token, 会话存 SQLite sessions 表(30天);登出删token;从 USERS 移除某人=吊销其全部会话 - updated_by 改由服务端按会话身份填写,前端自报 by 不再可信;登录框去掉昵称字段 - 登录失败全局递增节流(最多sleep 5s),口令比较用 secrets.compare_digest - Dockerfile/compose 移除一切口令默认值;compose 未设 STORY_WEB_USERS 直接报错 - 顺手修 playtest.js 走位/动画/out_ref 行未转义的存储型XSS(esc补齐)
This commit is contained in:
40
web/db.py
40
web/db.py
@ -1,8 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""M5 Web 编辑器的 SQLite 存储层。
|
||||
|
||||
单表 events:group(PK)/title/theme/status/ir_json/updated_at/updated_by/notes。
|
||||
表 events:group(PK)/title/theme/status/ir_json/updated_at/updated_by/notes。
|
||||
末次写入生效(设计接受),不做锁。
|
||||
表 sessions:token(PK)/user/expires_at——登录会话(cookie 只存随机 token,不存口令)。
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
@ -38,6 +39,43 @@ def init_db(path=None):
|
||||
notes TEXT
|
||||
)"""
|
||||
)
|
||||
c.execute(
|
||||
"""CREATE TABLE IF NOT EXISTS sessions (
|
||||
token TEXT PRIMARY KEY,
|
||||
user TEXT NOT NULL,
|
||||
expires_at REAL NOT NULL
|
||||
)"""
|
||||
)
|
||||
|
||||
|
||||
# ---------- 会话 ----------
|
||||
def create_session(token, user, expires_at, path=None):
|
||||
with _conn(path) as c:
|
||||
c.execute("INSERT OR REPLACE INTO sessions (token, user, expires_at) "
|
||||
"VALUES (?,?,?)", (token, user, expires_at))
|
||||
|
||||
|
||||
def get_session_user(token, now, path=None):
|
||||
"""token 有效返回用户名;过期则顺手删除并返回 None。"""
|
||||
with _conn(path) as c:
|
||||
r = c.execute("SELECT user, expires_at FROM sessions WHERE token=?",
|
||||
(token,)).fetchone()
|
||||
if not r:
|
||||
return None
|
||||
if r["expires_at"] < now:
|
||||
c.execute("DELETE FROM sessions WHERE token=?", (token,))
|
||||
return None
|
||||
return r["user"]
|
||||
|
||||
|
||||
def delete_session(token, path=None):
|
||||
with _conn(path) as c:
|
||||
c.execute("DELETE FROM sessions WHERE token=?", (token,))
|
||||
|
||||
|
||||
def purge_sessions(now, path=None):
|
||||
with _conn(path) as c:
|
||||
c.execute("DELETE FROM sessions WHERE expires_at < ?", (now,))
|
||||
|
||||
|
||||
def list_events(status=None, path=None):
|
||||
|
||||
Reference in New Issue
Block a user