2026-10-22 渗透测试报告(report.md)实测确认:统一入口的管理 API 在公网
完全未鉴权,可匿名改路由表、拆登录墙、改门户口令、写门户 HTML、读全部
浏览历史、做内网 SSRF 探测。根因有两层,本次一并修复。
## 根因 1:requireAdminToken 默认放行(代码层)
if !requireTokenForWrite { return true }
线上没有 NAVI_ADMIN_REQUIRE_TOKEN,于是所有"调了鉴权"的 handler 实际全部
放行——形同虚设。现在改为 fail-closed:
- 配了 NAVI_ADMIN_TOKEN → 校验 Bearer token
- 未配且显式 NAVI_ADMIN_ALLOW_UNAUTHENTICATED=1 → 放行(仅本机调试)
- 其它 → 401 拒绝
## 根因 2:login 域没有任何 auth_request(部署层)
kind=auth 的 server 块全站 requireLogin=false(它本身就是登录服务,这点
合理),但 location / 把 /api/、/admin/ 原样反代到 127.0.0.1:3005。两层
都没管 → 管理 API 裸奔。
新增 Route.AdminGuard:开启后 server 级挂 auth_request,只白名单放行登录
流程(/_portal/login|logout|verify、/login、/portal-page、favicon),
其余 /api/、/admin/ 一律要求已登录。
## 同时修掉的 4 个具体问题
1. /api/sync-info 明文回显 syncToken → 不再回显,且要求鉴权。
泄露该 token 等于浏览器历史的全读写权。
2. /api/history GET 无鉴权 → 与 DELETE 分支一致,要求管理员令牌。
3. /api/frameable 未鉴权 SSRF → 加鉴权 + 内网/回环黑名单。除 IP 字面量外
还对域名解析结果做校验,防止 localtest.me 之类指向 127.0.0.1 的绕过。
4. dashy 域 bypassPaths 里的 "/api/" 过宽 → 收窄到 "/api/history/"
(本意只放行浏览记录上报,实际把整个管理 API 暴露了)。
## 回归护栏
新增 admin-server/security_test.go(10 条)与 main_test_setup_test.go。
每条断言都是"改回旧行为就会失败",已用 3 次变异测试验证:
- 恢复默认放行 → TestAdminAPIFailsClosedByDefault FAIL
- sync-info 回显 token → TestSyncInfoDoesNotLeakToken FAIL
- 移除内网黑名单 → TestFrameableBlocksInternalTargets FAIL
- /api/ 豁免认证 → TestAuthKindWithAdminGuardProtectsAPI FAIL
- 不给 server 级 auth → 同上 FAIL
测试 56 项全绿。
## 顺带
searxng-settings.yml:停用 360search / ddgw / wikidata / wikipedia
(各自的实测理由见文件内注释),request_timeout 8.0→4.0。
Dockerfile:给 duckduckgo_web 引擎硬编码的 timeout=2 打补丁(经代理必超时)。
77 lines
2.8 KiB
YAML
77 lines
2.8 KiB
YAML
use_default_settings: true
|
||
general:
|
||
instance_name: "JianF Search"
|
||
debug: false
|
||
search:
|
||
safe_search: 0
|
||
autocomplete: "baidu"
|
||
default_lang: "zh-CN"
|
||
server:
|
||
port: 8888
|
||
bind_address: "127.0.0.1"
|
||
secret_key: "changeme-in-production"
|
||
image_proxy: true
|
||
ui:
|
||
static_use_hash: true
|
||
default_theme: simple
|
||
query_in_title: true
|
||
results_on_new_tab: false
|
||
redis:
|
||
url: false
|
||
outgoing:
|
||
# 走代理时握手与回源更慢。默认 3.0 / 4.0 会把一批引擎直接判为超时,
|
||
# 但调太大同样有害:SearXNG 会等所有引擎超时才返回,单个卡住的引擎
|
||
# 就能把整次搜索拖到 request_timeout 那么长(实测 8.0 时出现 19s 长尾)。
|
||
# 4.0/6.0 是折中:长尾收敛到 4s 左右,代价是慢引擎当次少结果。
|
||
request_timeout: 4.0
|
||
max_request_timeout: 6.0
|
||
pool_connections: 100
|
||
pool_maxsize: 20
|
||
# SearXNG curl_cffi 只认这个配置(不走 http_proxy 环境变量)
|
||
# 需要代理出网时取消注释并填写你的代理地址
|
||
# proxies:
|
||
# all://: http://your-proxy:7890
|
||
proxies: {}
|
||
engines:
|
||
- name: google
|
||
disabled: false
|
||
- name: bing
|
||
disabled: false
|
||
# www.bing.com 会 302 跳到 cn.bing.com,而 SearXNG 引擎请求不跟随重定向
|
||
# (allow_redirects=False)。结果只解析 178 字节的跳转页 -> 0 条且不报错,
|
||
# 表现为「Bing 结果异常少」。直接指向 cn.bing.com 可绕开重定向。
|
||
base_url: https://cn.bing.com
|
||
- name: duckduckgo
|
||
# html 端点(no-JS)实测被 DDG 反爬拦死:10/10 返回挑战页(202 + anomaly),
|
||
# 高密度请求后连 duckduckgo.com 主站也进入挑战态。改用下面的
|
||
# duckduckgo web(走 links.duckduckgo.com/d.js JSON API)。
|
||
disabled: true
|
||
- name: duckduckgo web
|
||
# 自带 firefox TLS 指纹 + default_headers=False,不受 no-JS 挑战页影响。
|
||
# 注意它依赖 Dockerfile 里对 _fetch_first_page_link 超时的补丁(上游硬编码
|
||
# timeout=2,经代理必超时 -> 引擎被挂起 -> 秒回 0 条)。
|
||
disabled: false
|
||
- name: 360search
|
||
# 实测单条查询 13~15s,且运行时 timeout=20s(高于全局),是长尾延迟的
|
||
# 主要来源。需要恢复时改回 false 并重启 searxng。
|
||
disabled: true
|
||
- name: wikipedia
|
||
disabled: false
|
||
- name: github
|
||
disabled: false
|
||
- name: npm
|
||
disabled: false
|
||
- name: pypi
|
||
disabled: false
|
||
# 实测上游不可用,默认关闭以避免无谓等待与错误日志:
|
||
# baidu -> 302 跳验证页(CAPTCHA,命中后引擎被封 1 小时)
|
||
# sogou -> 302 + 引擎自身 AttributeError('SXNG_Response' has no attribute 'next_request')
|
||
- name: baidu
|
||
disabled: true
|
||
- name: sogou
|
||
disabled: true
|
||
- name: startpage
|
||
disabled: true
|
||
- name: brave
|
||
disabled: true
|