Skip to content

fix(manager-server): prevent plugin resource auth elevation - #463

Merged
seakee merged 1 commit into
seakee:mainfrom
Cec1c:fix/issue-462-plugin-resource-auth
Jul 31, 2026
Merged

fix(manager-server): prevent plugin resource auth elevation#463
seakee merged 1 commit into
seakee:mainfrom
Cec1c:fix/issue-462-plugin-resource-auth

Conversation

@Cec1c

@Cec1c Cec1c commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes an authentication privilege escalation in the Manager Server plugin resource proxy. Requests to /v0/resource/plugins/* now use the saved CPA ManagementKey only after successful CPAMP admin authentication. Other requests preserve the caller's own authorization without privilege elevation.

修复 Manager Server 插件资源代理中的认证提权漏洞。此前,未认证的 GET/HEAD 请求可能被自动替换为服务器保存的 CPA ManagementKey,并以管理权限访问上游插件资源。

修复后,只有通过 CPAMP Admin Key 验证的请求才能使用保存的 CPA ManagementKey;其他请求只保留调用者自己的 Authorization,未提供认证时则以无认证状态转发。

Scope

  • Frontend panel
  • Manager Server
  • CPA panel mode
  • Full Docker mode
  • Native packages / release
  • Docs / Wiki
  • CI / build / tooling

Changes

  • Apply the same CPAMP admin-auth decision to GET, HEAD, POST, PUT, PATCH, and DELETE plugin resource requests.
  • Use the saved CPA ManagementKey only when the caller supplies a valid CPAMP Admin Key.
  • Preserve caller-provided Authorization headers for plugin-owned authentication flows.
  • Preserve unauthenticated access to genuinely public plugin resources without granting management privileges.
  • Add Mock upstream regression coverage for unauthenticated GET/HEAD, caller-auth GET, admin-auth GET, existing POST flows, and unsupported methods.

具体修改:

  • GET、HEAD、POST、PUT、PATCH、DELETE 统一执行 CPAMP Admin Key 身份判断。
  • 只有有效的 CPAMP Admin Key 才能授权 Manager Server 使用保存的 CPA ManagementKey。
  • 插件自己的 caller-auth 请求继续原样转发。
  • 无认证请求继续以无认证状态访问上游,由 CPA 或具体插件判断资源是否公开。
  • 增加 Mock upstream 回归测试,直接检查上游实际收到的 Authorization。
  • TRACE 等不支持的方法继续返回 HTTP 405。

Vulnerability Details / 漏洞信息

  • Vulnerability type / 漏洞类型: Authentication bypass and privilege escalation / 认证绕过与权限提升
  • CWE: CWE-287 Improper Authentication
  • Related behavior: Confused deputy / 权限代理
  • Affected component / 影响组件: Manager Server CPAResource handler
  • Affected route / 影响路径: /v0/resource/plugins/*
  • Attack type / 攻击方式: Remote, unauthenticated / 远程、无需认证
  • Fixed release / 安全版本: Not released yet / 尚未发布

Root Cause / 根本原因

The vulnerable implementation initialized useSavedManagementKey to true, but GET and HEAD requests did not execute AdminAuthService.VerifyHeader.

As a result, unauthenticated GET/HEAD requests retained useSavedManagementKey = true and were forwarded through ProxyPluginResource. The reverse-proxy Director then replaced the upstream Authorization header with the CPA ManagementKey stored by Manager Server.

原实现将 useSavedManagementKey 默认设置为 true,但 GET 和 HEAD 分支没有调用 AdminAuthService.VerifyHeader。因此,无认证 GET/HEAD 请求会保持 useSavedManagementKey = true,进入 ProxyPluginResource,随后由反向代理将 Manager Server 保存的 CPA ManagementKey 写入上游 Authorization。

req.Header.Set("Authorization", "Bearer "+setup.ManagementKey)

这使未认证客户端能够借用 Manager Server 的管理权限访问插件资源。

Vulnerable Request Flow / 漏洞调用链

Unauthenticated client / 未认证客户端
    │
    │ GET or HEAD /v0/resource/plugins/*
    ▼
CPAResource handler
    │
    │ GET/HEAD did not verify CPAMP Admin Key
    ▼
ProxyPluginResource
    │
    │ useSavedManagementKey = true
    ▼
Reverse proxy Director
    │
    │ Authorization: Bearer <saved CPA ManagementKey>
    ▼
CPA upstream / plugin resource
    │
    │ Response generated with management privileges
    ▼
Response returned to unauthenticated client

Affected Versions / 受影响版本

The release tags and handler implementations were checked directly.

已逐个检查正式 release tag 及对应 handler 实现。

Version / 版本 Status / 状态 Reason / 原因
<= v1.4.2 Not affected / 不受影响 CPAResource and /v0/resource/plugins/* proxy route did not exist / 尚未引入插件资源代理
v1.5.0 Affected / 受影响 Unauthenticated GET/HEAD called ProxyCPA, which injected the saved ManagementKey / 无认证 GET/HEAD 直接进入保存密钥代理
v1.6.0v1.11.10 Affected / 受影响 GET/HEAD retained useSavedManagementKey = true without admin verification / GET/HEAD 未认证且默认使用保存密钥
Fixed version / 安全版本 Not released yet / 尚未发布 Pending merge and release / 等待合并发布

The plugin resource proxy was introduced by commit faa1659f and first released in v1.5.0. In v1.5.0, CPAResource forwarded unauthenticated GET/HEAD requests through ProxyCPA, which always inserted the saved CPA ManagementKey.

Commit c5826633, first included in v1.6.0, introduced caller-auth support but retained the unauthenticated GET/HEAD saved-key path. All published releases from v1.5.0 through the current v1.11.10 are affected.

插件资源代理由提交 faa1659f 引入,并首次包含在 v1.5.0 中。首次进入 v1.6.0 的提交 c5826633 增加了 caller-auth 支持,但 GET/HEAD 仍然保留未认证使用保存密钥的路径。从 v1.5.0 到当前最新正式版本 v1.11.10,所有已发布版本均受影响。

Affected Deployment Modes / 受影响部署范围

Affected / 受影响:

  • Manager Server mode.
  • Full Docker deployments containing Manager Server.
  • Native Manager Server deployments.
  • Instances with a configured CPA upstream and CPA ManagementKey.
  • Instances where untrusted clients can reach the Manager Server.

具体包括 Manager Server 模式、包含 Manager Server 的 Full Docker 部署、原生 Manager Server 部署,以及已配置 CPA upstream、保存 CPA ManagementKey 且可被不可信客户端访问的实例。

Not affected or not remotely exploitable / 不受影响或无法远程利用:

  • CPA panel mode where CPA directly hosts the frontend without Manager Server.
  • Instances without a configured CPA upstream and saved CPA ManagementKey.
  • Instances reachable only from localhost or a trusted private network are not directly remotely exploitable, although affected versions still contain the vulnerable code path.

Impact / 漏洞影响

This issue does not directly return the CPA ManagementKey string to the unauthenticated caller. Instead, the caller's request is executed with the privileges of the saved ManagementKey, and the privileged upstream response is returned to the caller.

The exposed data depends on installed plugins and their resource handlers. Potential impact includes unauthorized access to management-protected plugin configuration, account/provider information, sensitive plugin status data, or credentials if a plugin resource returns them under management authorization.

The resource path is not a generic file-read primitive. The built-in /v0/management/auth-files endpoint is not directly exposed through this route.

本漏洞不会直接把 CPA ManagementKey 字符串返回给攻击者。实际问题是攻击者的请求会借用该密钥的权限执行,并取得管理权限下的插件响应。最终可泄露的数据取决于已安装插件及其资源 handler;该路径不是任意文件读取接口,也不能直接访问内置的 /v0/management/auth-files

Fix / 修复方案

The fix changes credential substitution from a method-dependent default to an explicit authorization decision.

Caller / 调用者 Authorization sent upstream / 发给上游的认证
No Authorization / 无认证 No Authorization / 不携带认证
Plugin caller-auth / 插件自己的认证 Original caller Authorization / 原样转发
Valid CPAMP Admin Key / 有效 CPAMP 管理员密钥 Saved CPA ManagementKey / 使用保存的 CPA 管理密钥
Unsupported method / 不支持的方法 Not proxied; HTTP 405 / 不转发,返回 405

The core security invariant is:

Manager Server may use the saved CPA ManagementKey on behalf of a request only after validating a CPAMP Admin Key.

核心安全约束是:

Manager Server 只有在确认调用者持有有效 CPAMP Admin Key 后,才能代表调用者使用保存的 CPA ManagementKey。

User Impact

Public plugin resources remain accessible according to the upstream CPA/plugin policy. Plugin caller-auth flows remain compatible. Unauthenticated callers can no longer have their requests elevated with the Manager Server's saved CPA ManagementKey.

公开插件资源仍然可以按照 CPA upstream 或具体插件的权限规则访问,插件自身的 caller-auth 流程保持兼容。用户侧唯一安全行为变化是:未通过 CPAMP 管理员认证的请求不再获得隐式 CPA 管理权限。

Compatibility / Runtime Notes

  • CPA panel mode: Unaffected; the vulnerable handler exists only in Manager Server mode.
  • Manager Server mode: Saved CPA credentials are now used only for requests authenticated with a valid CPAMP Admin Key.
  • Full Docker / native packages: No configuration changes or data migrations are required.

公开插件资源不会被统一强制为 CPAMP Admin Key;插件 caller-auth 请求继续保留原始 Authorization。Full Docker 和原生部署无需修改配置,也不需要数据迁移。

Data / Security Notes

The saved CPA ManagementKey remains encrypted at rest. Requests that do not pass CPAMP admin authentication are forwarded only with their original Authorization header, or without Authorization when none was supplied.

No secrets are added to logs or error responses by this change. No SQLite, auth-file, configuration, or stored-secret migration is involved.

保存的 CPA ManagementKey 仍然保持静态加密存储。未通过 CPAMP 管理员认证的请求只会携带其原始 Authorization;未提供 Authorization 时,上游请求也不会包含认证信息。本次修改不会向日志或错误响应添加密钥,也不涉及 SQLite、认证文件或配置迁移。

Risk / Rollback

Risk level: Medium

The main compatibility risk is changing how GET/HEAD plugin resource requests select their upstream Authorization. Regression tests cover unauthenticated public resources, caller-auth plugin resources, CPAMP-admin-authenticated resources, existing POST/PUT behavior, and unsupported HTTP methods.

主要兼容性风险是 GET/HEAD 插件资源请求的 Authorization 选择逻辑发生变化。回归测试已经覆盖无认证公开资源、插件 caller-auth、CPAMP Admin Key、现有 POST/PUT 行为和不支持的 HTTP 方法。

Rollback notes:

Reverting this change restores the previous proxy behavior and reopens the privilege-escalation path. Rollback is not recommended for an externally reachable Manager Server.

If an immediate upgrade is unavailable, temporarily restrict Manager Server access to localhost, a trusted private network, VPN, or an authenticated reverse proxy.

回滚会恢复旧代理行为并重新打开权限提升路径。如果暂时无法升级,应将 Manager Server 限制为仅 localhost、可信内网、VPN 或受保护的反向代理可访问。

Verification

  • Type check
  • Lint
  • Tests
  • Build
  • Manual UI check
  • Docs/link check
  • Not applicable, docs-only

Commands / evidence:

go test ./internal/httpapi ./internal/service/proxy -count=1
go build ./cmd/cpa-manager-plus
go vet ./internal/httpapi ./internal/service/proxy
git diff --check

Mock upstream authorization verification:

Unauthenticated GET  -> upstream Authorization is empty
Unauthenticated HEAD -> upstream Authorization is empty
Caller-auth GET      -> caller Authorization is preserved
CPAMP Admin GET      -> saved CPA ManagementKey is used
Unsupported method   -> HTTP 405

Results:

go test ./internal/httpapi ./internal/service/proxy -count=1  PASS
go build ./cmd/cpa-manager-plus                        PASS
go vet ./internal/httpapi ./internal/service/proxy     PASS
git diff --check                                       PASS

go test ./... passes all relevant packages. The existing Windows-only data-key permission assertion reports mode 0666 instead of 0600; this failure is unrelated to the plugin proxy change:

TestLoadOrCreateDataKeyCreatesStableRestrictedFile
data key permissions = 666, want 600

Screenshots / Recordings

N/A — backend-only security fix.

本次修改不涉及前端页面或可见 UI。

Docs

  • README / README_CN updated for user-visible capabilities
  • Matching docs manual and navigation updated
  • Demo fixtures, screenshots, and deep links reviewed
  • Release notes needed
  • Not needed — explanation included below

Docs decision:

No user-guide changes are required. The fix should be documented in the next security/release notes.

下一版本发布说明建议注明 v1.5.0v1.11.10 受影响;使用 Manager Server、Full Docker 或原生完整模式的用户应尽快升级。曾将 Manager Server 暴露给不可信网络的用户应检查访问日志和已安装插件;如果插件资源可能返回敏感凭证,应在升级后轮换相关 API Key 或 Token。

Related

Fixes #462

@Cec1c

Cec1c commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

关于POC:我可以私有仓库+私发token形式给维护者先确认
或者觉得没必要的话我直接放出来

@seakee

seakee commented Jul 31, 2026

Copy link
Copy Markdown
Owner

关于POC:我可以私有仓库+私发token形式给维护者先确认 或者觉得没必要的话我直接放出来

感谢详细排查与修复。我们已复核 #462 的实际代码路径;当前 PR head 的 Manager Server、Windows SQLite、Docker Build 和 PR Template 检查均已通过。

该修复正确地让未通过 CPAMP Admin Key 验证的 resource 请求保留调用方原始 Authorization(或无 Authorization),避免 GET/HEAD 被替换为保存的 CPA Management Key,同时保持插件 caller-auth 兼容。因此该 PR 已具备合并条件,我们会将修复纳入下一个安全版本。

关于 POC:核心漏洞已确认,当前不需要公开可直接利用的 POC,也请不要私发任何真实 token、Admin Key、敏感响应或生产环境日志。若你发现某个具体插件存在额外影响,可以通过私有仓库提供完全脱敏的最小复现,例如 mock upstream、一次性测试值和非真实数据

@seakee
seakee merged commit 842eec7 into seakee:main Jul 31, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Authentication Bypass in CPA Plugin Resource Proxy (CPAResource) — Unauthenticated GET/HEAD to Plugin Resources

2 participants