security: 公网生产环境加固 — P0/P1 全部修复
API 认证 (16 个未保护端点 → 全部加 panel_auth): - 文件管理器: 11 端点 (list/mkdir/delete/upload/download/read/write/...) - 项目管理: 6 端点 (list/run/stop/logs/stdin/page), 修复硬编码路径前缀 - 代理管理: 3 端点 (list/add/remove) - 系统状态: 2 HTTP + 1 WS (token 校验) - 插件页面: 3 端点 (page/sse/event) - 内部路由: 3 端点 (plugins/commands/data) 认证系统加固: - 密码哈希: 固定盐 → 每用户独立 secrets.token_hex(16) 随机盐 - 默认密码警告: 启动时检测并打印 critical 级别日志 - 登录频率限制: 5 次失败 / IP → 锁定 60 秒, 返回 429 基础设施: - 安全响应头: X-Content-Type-Options/X-Frame-Options/X-XSS-Protection/Referrer-Policy - WebSocket 鉴权: query string token 校验 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+34
-19
@@ -17,6 +17,7 @@ class User:
|
||||
"""用户数据类"""
|
||||
username: str
|
||||
password_hash: str
|
||||
salt: str
|
||||
permissions: List[str]
|
||||
is_active: bool = True
|
||||
created_at: float = None
|
||||
@@ -48,36 +49,48 @@ class AuthService:
|
||||
def _init_default_users(self):
|
||||
"""初始化默认用户"""
|
||||
try:
|
||||
# 创建默认管理员用户
|
||||
admin_password_hash = self._hash_password(os.environ.get("SENSU_ADMIN_PASSWORD","admin123"))
|
||||
# 管理员 — 独立随机盐
|
||||
admin_salt = secrets.token_hex(16)
|
||||
admin_pw = os.environ.get("SENSU_ADMIN_PASSWORD", "admin123")
|
||||
admin_user = User(
|
||||
username="admin",
|
||||
password_hash=admin_password_hash,
|
||||
password_hash=self._hash_password(admin_pw, admin_salt),
|
||||
salt=admin_salt,
|
||||
permissions=["admin"],
|
||||
created_at=time.time()
|
||||
created_at=time.time(),
|
||||
)
|
||||
self.users["admin"] = admin_user
|
||||
|
||||
# 创建默认API用户
|
||||
api_password_hash = self._hash_password(os.environ.get("SENSU_API_PASSWORD","api123"))
|
||||
|
||||
# API 用户 — 独立随机盐
|
||||
api_salt = secrets.token_hex(16)
|
||||
api_pw = os.environ.get("SENSU_API_PASSWORD", "api123")
|
||||
api_user = User(
|
||||
username="api",
|
||||
password_hash=api_password_hash,
|
||||
password_hash=self._hash_password(api_pw, api_salt),
|
||||
salt=api_salt,
|
||||
permissions=["framework.status.read", "plugin.info.read"],
|
||||
created_at=time.time()
|
||||
created_at=time.time(),
|
||||
)
|
||||
self.users["api"] = api_user
|
||||
|
||||
logger.debug("默认用户初始化完成")
|
||||
|
||||
|
||||
logger.debug("默认用户初始化完成 (独立随机盐)")
|
||||
|
||||
# 安全警告 — 仍在使用默认密码
|
||||
if admin_pw == "admin123" or api_pw == "api123":
|
||||
logger.critical(
|
||||
"⚠️ 安全警告: 正在使用默认密码! "
|
||||
"请设置环境变量 SENSU_ADMIN_PASSWORD 和 SENSU_API_PASSWORD"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"初始化默认用户时出错: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
def _hash_password(self, password: str) -> str:
|
||||
"""哈希密码"""
|
||||
def _hash_password(self, password: str, salt: str = None) -> str:
|
||||
"""哈希密码 — 每个用户独立随机盐"""
|
||||
try:
|
||||
salt = "catframework_salt" # 实际应该使用随机盐
|
||||
if salt is None:
|
||||
salt = secrets.token_hex(16)
|
||||
return hashlib.sha256((password + salt).encode()).hexdigest()
|
||||
except Exception as e:
|
||||
logger.error(f"哈希密码时出错: {str(e)}", exc_info=True)
|
||||
@@ -98,7 +111,7 @@ class AuthService:
|
||||
logger.warning(f"用户已被禁用: {username}")
|
||||
return None
|
||||
|
||||
password_hash = self._hash_password(password)
|
||||
password_hash = self._hash_password(password, user.salt)
|
||||
if user.password_hash != password_hash:
|
||||
logger.warning(f"密码错误: {username}")
|
||||
return None
|
||||
@@ -203,13 +216,15 @@ class AuthService:
|
||||
if username in self.users:
|
||||
logger.warning(f"用户已存在: {username}")
|
||||
return False
|
||||
|
||||
password_hash = self._hash_password(password)
|
||||
|
||||
salt = secrets.token_hex(16)
|
||||
password_hash = self._hash_password(password, salt)
|
||||
user = User(
|
||||
username=username,
|
||||
password_hash=password_hash,
|
||||
salt=salt,
|
||||
permissions=permissions,
|
||||
created_at=time.time()
|
||||
created_at=time.time(),
|
||||
)
|
||||
|
||||
self.users[username] = user
|
||||
|
||||
Reference in New Issue
Block a user