security: Sentinel 路由鉴权 + API Key CSRF + SSRF 防护

修复:
- Sentinel 路由 require_auth=False → True (严重: 未认证可访问)
- API Key 创建/删除加 csrf_protect=True
- SSRF 防护: 测试连接仅允许 http/https scheme
- 插件网络鉴权: 已加载插件自动放行 (不再要求 plugin.network.access)
- Sentinel 权限列表补充 plugin.network.access

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
qinglong
2026-06-13 15:13:56 +08:00
parent 5e74b0aad7
commit 634496d975
9 changed files with 71 additions and 36 deletions
+11 -3
View File
@@ -66,7 +66,7 @@ class Plugin(PluginWebMixin):
("POST", "/api/nodes/delete", self._handle_delete_node),
]:
await self.network_bridge.register_http_route(
path, handler, methods=[method], require_auth=False
path, handler, methods=[method], require_auth=True
)
# 启动后台轮询
@@ -158,9 +158,17 @@ class Plugin(PluginWebMixin):
from aiohttp import web
try:
data = await request.json()
node = {"url": data.get("url", "").strip(), "api_key": data.get("api_key", "").strip()}
if not node["url"]:
raw_url = data.get("url", "").strip()
if not raw_url:
return web.json_response({"ok": False, "error": "URL 为空"}, status=400)
# SSRF 防护: 仅允许 HTTP/HTTPS
from urllib.parse import urlparse
parsed = urlparse(raw_url)
if parsed.scheme not in ("http", "https"):
return web.json_response({"ok": False, "error": "仅允许 HTTP/HTTPS"}, status=403)
if not parsed.hostname:
return web.json_response({"ok": False, "error": "无效的 URL"}, status=400)
node = {"url": raw_url.rstrip("/"), "api_key": data.get("api_key", "").strip()}
ok, info, err = await self._fetch_system(node)
return web.json_response({"ok": ok, "system": info, "error": err})
except Exception as e: