openclaw - ✅(Solved) Fix [Bug]: shouldSkipBackendSelfPairing allows loopback clients to self-declare GATEWAY_CLIENT identity and bypass device pairing [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#72418Fetched 2026-04-27 05:30:21
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
cross-referenced ×1

Root Cause

Demonstrated Impact

Root cause: shouldSkipLocalBackendSelfPairing trusts connectParams.client.id and connectParams.client.mode as identity signals without server-side verification. These fields are populated from the client's WS connect payload and are entirely attacker-controlled. Any local process with the gateway token can set these fields to the magic backend identity values and receive a pairing bypass.

Fix Action

Fixed

PR fix notes

PR #72419: fix: shouldSkipBackendSelfPairing allows loopback clients to...

Description (problem / solution / changelog)

Summary

  • Problem: A local process or SSRF-reachable service that possesses a valid gateway token can impersonate the internal backend client identity, skip device pairing entirely, and connect as a fully-paired operator session without holding a registered device token — combining with the scope self-declaration issue to gain unrestricted gateway access.
  • Why it matters: From a loopback address (127.0.0.1 or ::1), open a WebSocket connection to the gateway without a Origin header.
  • What changed: From a loopback address (127.0.0.1 or ::1), open a WebSocket connection to the gateway without a Origin header.
  • What did NOT change (scope boundary): No unrelated defaults, migrations, or compatibility behavior were intentionally changed.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #72418
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: From a loopback address (127.0.0.1 or ::1), open a WebSocket connection to the gateway without a Origin header.
  • Missing detection / guardrail: No narrower detection note was recorded in the issue bundle.
  • Contributing context (if known): See the linked issue analysis and changed files below.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this: Validation command pnpm build && pnpm check && pnpm test
  • Target test or file: Not explicitly recorded in the issue bundle.
  • Scenario the test should lock in: A local process or SSRF-reachable service that possesses a valid gateway token can impersonate the internal backend client identity, skip device pairing entirely, and connect as a fully-paired operator session without holding a registered device token — combining with the scope self-declaration issue to gain unrestricted gateway access.
  • Why this is the smallest reliable guardrail: It validates the failing behavior on the touched path without expanding scope.
  • Existing test that already covers this (if any): Unknown.
  • If no new test is added, why not: The staged bundle did not record a narrower regression target.

User-visible / Behavior Changes

  • None beyond resolving the linked issue's broken behavior.

Diagram (if applicable)

N/A

Security Impact (required)

  • New permissions/capabilities? (Yes/No): No
  • Secrets/tokens handling changed? (Yes/No): Yes
  • New/changed network calls? (Yes/No): No
  • Command/tool execution surface changed? (Yes/No): Yes
  • Data access scope changed? (Yes/No): Yes
  • If any Yes, explain risk + mitigation: From a loopback address (127.0.0.1 or ::1), open a WebSocket connection to the gateway without a Origin header.. Mitigation: pnpm build && pnpm check && pnpm test reported passed.

Repro + Verification

Environment

  • OS: N/A
  • Runtime/container: N/A
  • Model/provider: opencode/glm-5.1
  • Integration/channel (if any): N/A
  • Relevant config (redacted): AI-assisted=yes

Steps

  1. Reproduce the linked issue using the recorded issue bundle.
  2. Apply the fix from this branch.
  3. Run pnpm build && pnpm check && pnpm test.

Expected

  • A local process or SSRF-reachable service that possesses a valid gateway token can impersonate the internal backend client identity, skip device pairing entirely, and connect as a fully-paired operator session without holding a registered device token — combining with the scope self-declaration issue to gain unrestricted gateway access.
  • Validation passes for the touched behavior.

Actual

  • Validation status: passed

Evidence

  • Validation evidence: pnpm build && pnpm check && pnpm test reported passed.
  • CVSS v3.1: 8.7 (High)
  • CVSS v4.0: 9.3 (Critical)
  • Changed files:
  • src/gateway/server/ws-connection/handshake-auth-helpers.test.ts (+10/-1)
  • src/gateway/server/ws-connection/handshake-auth-helpers.ts (+2/-1)

Human Verification (required)

  • Verified scenarios: A local process or SSRF-reachable service that possesses a valid gateway token can impersonate the internal backend client identity, skip device pairing entirely, and connect as a fully-paired operator session without holding a registered device token — combining with the scope self-declaration issue to gain unrestricted gateway access.
  • Edge cases checked: pnpm build && pnpm check && pnpm test completed with status passed.
  • What you did not verify: Interactive/manual scenarios not captured in the staged issue bundle.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No): Yes
  • Config/env changes? (Yes/No): No
  • Migration needed? (Yes/No): No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: Regression in the touched behavior while closing the linked issue.
    • Mitigation: pnpm build && pnpm check && pnpm test reported passed and the changed-file scope stayed limited to the recorded diff.

Changed files

  • src/gateway/server/ws-connection/handshake-auth-helpers.test.ts (modified, +18/-0)
  • src/gateway/server/ws-connection/handshake-auth-helpers.ts (modified, +7/-1)

Code Example

export function shouldSkipLocalBackendSelfPairing(params: {
  connectParams: ConnectParams;
  locality: PairingLocalityKind;
  hasBrowserOriginHeader: boolean;
  sharedAuthOk: boolean;
  authMethod: GatewayAuthResult["method"];
}): boolean {
  const isBackendClient =
    params.connectParams.client.id === GATEWAY_CLIENT_IDS.GATEWAY_CLIENT &&
    params.connectParams.client.mode === GATEWAY_CLIENT_MODES.BACKEND;
  if (!isBackendClient) {
    return false;
  }
  const usesSharedSecretAuth = params.authMethod === "token" || params.authMethod === "password";
  const usesDeviceTokenAuth = params.authMethod === "device-token";
  return (
    params.locality === "direct_local" &&
    !params.hasBrowserOriginHeader &&
    ((params.sharedAuthOk && usesSharedSecretAuth) || usesDeviceTokenAuth)
  );
}
RAW_BUFFERClick to expand / collapse

Severity Assessment

CVSS Assessment

Metricv3.1v4.0
Score8.7 / 10.09.3 / 10.0
SeverityHighCritical
VectorCVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:NCVSS:4.0/AV:A/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N
CalculatorCVSS v3.1 CalculatorCVSS v4.0 Calculator

Threat Model Alignment

Classification: security-specific

The trust boundary violated here is between the gateway server and any local process holding a valid shared secret token. The gateway assumes that a loopback connection bearing the correct token and self-declaring backend client identity must be the legitimate backend service. In reality, any co-located process (or SSRF chain reaching loopback) can fabricate these client-declared fields and receive the same pairing bypass. The boundary between "trusted internal backend" and "any token-holding local process" is not enforced by a server-side identity check or a distinct credential — only by client-supplied connect parameters.

Impact

A local process or SSRF-reachable service that possesses a valid gateway token can impersonate the internal backend client identity, skip device pairing entirely, and connect as a fully-paired operator session without holding a registered device token — combining with the scope self-declaration issue to gain unrestricted gateway access.

Affected Component

File: openclaw/src/gateway/server/ws-connection/handshake-auth-helpers.ts:252

export function shouldSkipLocalBackendSelfPairing(params: {
  connectParams: ConnectParams;
  locality: PairingLocalityKind;
  hasBrowserOriginHeader: boolean;
  sharedAuthOk: boolean;
  authMethod: GatewayAuthResult["method"];
}): boolean {
  const isBackendClient =
    params.connectParams.client.id === GATEWAY_CLIENT_IDS.GATEWAY_CLIENT &&
    params.connectParams.client.mode === GATEWAY_CLIENT_MODES.BACKEND;
  if (!isBackendClient) {
    return false;
  }
  const usesSharedSecretAuth = params.authMethod === "token" || params.authMethod === "password";
  const usesDeviceTokenAuth = params.authMethod === "device-token";
  return (
    params.locality === "direct_local" &&
    !params.hasBrowserOriginHeader &&
    ((params.sharedAuthOk && usesSharedSecretAuth) || usesDeviceTokenAuth)
  );
}

Technical Reproduction

  1. From a loopback address (127.0.0.1 or ::1), open a WebSocket connection to the gateway without a Origin header.
  2. Set connectParams.client.id = GATEWAY_CLIENT_IDS.GATEWAY_CLIENT and connectParams.client.mode = GATEWAY_CLIENT_MODES.BACKEND in the connect payload. Include a valid gateway token in connectParams.auth.token.
  3. shouldSkipLocalBackendSelfPairing evaluates: isBackendClient = true (client ID and mode match), locality = "direct_local" (loopback), hasBrowserOriginHeader = false (no Origin header), sharedAuthOk = true (valid token), usesSharedSecretAuth = true. Returns true.
  4. Device pairing is skipped. The session continues as a fully-authenticated, paired operator session without device registration.
  5. Combined with Finding AA-rem (scope self-declaration), send connectParams.scopes = ["operator.admin"]; both pairing and scope restrictions are bypassed simultaneously.

Demonstrated Impact

Root cause: shouldSkipLocalBackendSelfPairing trusts connectParams.client.id and connectParams.client.mode as identity signals without server-side verification. These fields are populated from the client's WS connect payload and are entirely attacker-controlled. Any local process with the gateway token can set these fields to the magic backend identity values and receive a pairing bypass.

The intended scope of this bypass is the legitimate internal backend service (the same process or a sibling service that already holds the token). However, the bypass is enforced purely on the basis of client-declared fields and connection metadata, not on a verifiable out-of-band registration or cryptographic identity. This makes it reachable by any token-holder from localhost, including other applications, scripts, or SSRF attack chains that can originate a loopback connection.

Existing controls: the locality === "direct_local" && !hasBrowserOriginHeader check constrains the attack to loopback-origin connections, which provides some defense-in-depth but does not eliminate the risk for multi-tenant or shared-host environments where multiple processes have access to the token and loopback.

Environment

Verified against openclaw/openclaw release v2026.4.24 (commit a3c51f91c58a76fa3dbd9e808071d0b41c981649, published 2026-04-25T18:15:17Z). Observed in gateway source at openclaw/src/gateway/server/ws-connection/handshake-auth-helpers.ts (function shouldSkipLocalBackendSelfPairing, line 252). Exploitable from any process on the same host as the gateway that holds a valid shared token.

Remediation Advice

The backend self-pairing bypass should not rely solely on client-declared client.id and client.mode fields. Consider binding the backend bypass to a separately configured server-side allowlist of known backend client identifiers, or use a dedicated bootstrap token distinct from the shared operator token so that backend-identity bypass is only possible with a credential that is not exposed to general operator use.

<!-- submission-marker:AA-nzb-backend-client-pairing-bypass -->

extent analysis

TL;DR

The most likely fix is to modify the shouldSkipLocalBackendSelfPairing function to verify the backend client identity through a server-side allowlist or a dedicated bootstrap token, rather than relying solely on client-declared fields.

Guidance

  • Identify the trusted backend client identifiers and create a server-side allowlist to validate against.
  • Consider introducing a dedicated bootstrap token for backend clients, separate from the shared operator token.
  • Modify the shouldSkipLocalBackendSelfPairing function to check the allowlist or validate the bootstrap token before skipping device pairing.
  • Review the existing controls, such as the locality and hasBrowserOriginHeader checks, to ensure they are still effective with the new verification mechanism.

Example

const trustedBackendClients = ['known-backend-client-1', 'known-backend-client-2'];

export function shouldSkipLocalBackendSelfPairing(params: {
  connectParams: ConnectParams;
  locality: PairingLocalityKind;
  hasBrowserOriginHeader: boolean;
  sharedAuthOk: boolean;
  authMethod: GatewayAuthResult["method"];
}): boolean {
  const isTrustedBackendClient = trustedBackendClients.includes(params.connectParams.client.id);
  // ...
}

Notes

The provided code snippet is a simplified example and may require additional modifications to fit the specific use case. It is essential to thoroughly test the changes to ensure the security fix does not introduce any unintended behavior.

Recommendation

Apply a workaround by implementing a server-side allowlist or dedicated bootstrap token to verify backend client identities, as this approach addresses the root cause of the issue and provides a more secure solution.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: shouldSkipBackendSelfPairing allows loopback clients to self-declare GATEWAY_CLIENT identity and bypass device pairing [1 pull requests, 1 participants]