Openclaw · EN · Part 3

Ch 12 Security And Privacy

12-ch-12-Security-and-Privacy.md

第12章 Security and Privacy

Security Philosophy

OpenClaw's security model begins with a deliberate architectural choice: local-first as a security feature. By keeping processing on your hardware rather than shipping prompts to third-party servers, you eliminate entire classes of vulnerabilities—data leakage in transit, provider-side breaches, and opaque retention policies. This isn't an accident; it's the foundational design principle.

🤿 深入底层:The Gateway control plane (src/gateway/) implements a typed WebSocket protocol with challenge-response authentication. Every connection must prove identity before receiving a session handle. This design prevents anonymous connections from accessing control surfaces.

The DM pairing model reinforces this philosophy. When someone messages your assistant for the first time, they don't automatically gain access—they must be on your allowlist. This explicit authorization step creates a security boundary at the entry point, before any message reaches your agent's context.

// src/channels/allowlist-match.ts

export function resolveAllowlistMatchSimple(params: {
  allowFrom: ReadonlyArray<string | number>;
  senderId: string;
  senderName?: string | null;
  allowNameMatching?: boolean;
}): AllowlistMatch<"wildcard" | "id" | "name"> {
  const allowFrom = compileSimpleAllowlist(params.allowFrom); # [1]
  
  if (allowFrom.set.size === 0) {
    return { allowed: false }; # [2]
  }
  if (allowFrom.wildcard) {
    return { allowed: true, matchKey: "*", matchSource: "wildcard" }; # [3]
  }
  // ... matching logic
}

The function compiles allowlists into efficient Set lookups (#1), defaults to rejection when empty (#2), and supports wildcard entries for trusted environments (#3). This is security through explicit authorization rather than implicit trust.

OpenClaw's threat model makes critical assumptions:

Rendering diagram...

The model explicitly does not treat one gateway as a multi-tenant adversarial boundary. From SECURITY.md:

"OpenClaw does not model one gateway as a multi-tenant, adversarial user boundary. Authenticated Gateway callers are treated as trusted operators for that gateway instance."

This means session identifiers (sessionKey, IDs, labels) are routing controls, not authorization boundaries. If one operator can view another operator's data on the same gateway, that's expected behavior—use separate gateways (or separate OS users/hosts) for separate trust boundaries.

Authentication and Authorization

OpenClaw implements layered authentication. Every Gateway connection must pass through multiple verification gates before receiving any capabilities.

Device Authentication

Device authentication establishes cryptographically-verifiable identity for connecting clients. The handshake uses challenge-response signing with device-specific keys:

// src/gateway/device-auth.ts

export function buildDeviceAuthPayloadV3(params: DeviceAuthV3Params): string {
  const scopes = params.scopes.join(",");
  const token = params.token ?? "";
  const platform = normalizeDeviceMetadataForAuth(params.platform);
  const deviceFamily = normalizeDeviceMetadataForAuth(params.deviceFamily);
  
  return [
    "v3",
    params.deviceId,
    params.clientId,
    params.clientMode,
    params.role,
    scopes,
    String(params.signedAtMs),
    token,
    params.nonce,
    platform,
    deviceFamily,
  ].join("|");
}

The payload includes versioning (v3), device identifiers, role declarations, scope requests, and anti-replay timestamps. The Gateway verifies signatures and validates that requested scopes don't exceed the device's authorized capabilities.

Channel Pairing

Before a channel connection can deliver messages, it must complete the pairing handshake. This exchange proves control over the channel account (e.g., Telegram bot token, Discord webhook) and establishes the allowlist for incoming messages.

The pairing decision (src/gateway/server.ws-connection/connect-policy.ts) considers multiple factors:

export function evaluateMissingDeviceIdentity(params: {
  hasDeviceIdentity: boolean;
  role: GatewayRole;
  isControlUi: boolean;
  controlUiAuthPolicy: ControlUiAuthPolicy;
  trustedProxyAuthOk?: boolean;
  sharedAuthOk: boolean;
  authOk: boolean;
  hasSharedAuth: boolean;
  isLocalClient: boolean;
}): MissingDeviceIdentityDecision {
  if (params.hasDeviceIdentity) {
    return { kind: "allow" };
  }
  if (params.isControlUi && params.controlUiAuthPolicy.allowBypass && params.role === "operator") {
    // dangerouslyDisableDeviceAuth: true — operator has explicitly opted out
    return { kind: "allow" };
  }
  // ... additional policy checks
  return { kind: "reject-device-required" };
}

Note the dangerouslyDisableDeviceAuth path—it exists for legitimate localhost development scenarios, but the naming signals the security tradeoff.

⚠️ 避坑指南:The dangerouslyDisableDeviceAuth configuration option is exactly what it sounds like—a break-glass mechanism that disables device identity verification. Never use this on network-exposed deployments. The security audit (openclaw security audit) flags this as a critical finding when enabled outside localhost.

Provider Credentials

Model provider credentials (API keys, OAuth tokens) live under ~/.openclaw/credentials/ and are never committed to repositories. OpenClaw uses SecretRef semantics for runtime credential resolution, allowing references like ${secret:openai:sk-...} in configuration without plaintext values in openclaw.json.

The Gateway resolves these references through a dedicated secret-handling path that requires @openclaw/secops approval for modifications, as documented in .github/CODEOWNERS:

// .github/CODEOWNERS

# Security-sensitive code, config, and docs require secops review.
/src/gateway/*secret*.ts @openclaw/secops
/src/gateway/**/*secret*.ts @openclaw/secops
/src/gateway/resolve-configured-secret-input-string*.ts @openclaw/secops

This creates a human-in-the-loop requirement for changes to credential handling, ensuring security review before any modifications land.

Policy Enforcement

Allowlists and access controls implement the authorization layer—determining who can trigger your agent and what they can access once connected.

Allowlists

OpenClaw supports multiple allowlist sources that merge with well-defined precedence:

// src/channels/allow-from.ts

export function mergeDmAllowFromSources(params: {
  allowFrom?: Array<string | number>;
  storeAllowFrom?: Array<string | number>;
  dmPolicy?: string;
}): string[] {
  const storeEntries = params.dmPolicy === "allowlist" ? [] : (params.storeAllowFrom ?? []);
  return [...(params.allowFrom ?? []), ...storeEntries]
    .map((value) => String(value).trim())
    .filter(Boolean);
}

When dmPolicy is set to "allowlist", stored allowlist entries are ignored—requiring explicit per-channel configuration. This implements a secure-by-default approach where new channels don't inherit potentially stale authorization lists.

Group message handling provides additional flexibility with fallback modes:

export function resolveGroupAllowFromSources(params: {
  allowFrom?: Array<string | number>;
  groupAllowFrom?: Array<string | number>;
  fallbackToAllowFrom?: boolean;
}): string[] {
  const explicitGroupAllowFrom =
    Array.isArray(params.groupAllowFrom) && params.groupAllowFrom.length > 0
      ? params.groupAllowFrom
      : undefined;
  const scoped = explicitGroupAllowFrom
    ? explicitGroupAllowFrom
    : params.fallbackToAllowFrom === false
      ? []
      : (params.allowFrom ?? []);
  return scoped.map((value) => String(value).trim()).filter(Boolean);
}

The fallbackToAllowFrom parameter controls whether groups without explicit allowlists inherit the DM allowlist—providing a choice between strict isolation (no fallback) and convenience (inherit DM settings).

Access Controls

Command gating implements fine-grained authorization for control commands:

// src/channels/command-gating.ts

export function resolveControlCommandGate(params: {
  useAccessGroups: boolean;
  authorizers: CommandAuthorizer[];
  allowTextCommands: boolean;
  hasControlCommand: boolean;
  modeWhenAccessGroupsOff?: CommandGatingModeWhenAccessGroupsOff;
}): { commandAuthorized: boolean; shouldBlock: boolean } {
  const commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
    useAccessGroups: params.useAccessGroups,
    authorizers: params.authorizers,
    modeWhenAccessGroupsOff: params.modeWhenAccessGroupsOff,
  });
  const shouldBlock = params.allowTextCommands && 
                      params.hasControlCommand && 
                      !commandAuthorized;
  return { commandAuthorized, shouldBlock };
}

The CommandAuthorizer interface supports multiple authorization sources (access groups, allowlists, role-based checks) that compose together. This allows complex policies like "allow text commands for access-group members who are also DM-allowlisted."

Channel-Specific Policies

Different channels have different security characteristics. Telegram provides verified user IDs; Slack webhooks can be spoofed; iMessage requires device pairing. OpenClaw respects these differences through channel-specific policy enforcement:

  • DM Policy: Controls who can trigger your agent in direct messages (dmPolicy: "allowlist" or "open")
  • Group Policy: Controls group chat access with groupAllowFrom arrays
  • Mention Gates: Restrict bot responses to explicit mentions in group settings

☕ 茶水间时间:Why does OpenClaw default to allowlist mode for DMs? The answer lies in the asymmetry of messaging platforms. Anyone can message your Telegram bot if they know the username, but only allowlisted users actually reach your agent. This prevents spam and accidental triggers while preserving the ability to open up when needed.

Enforcement Points

Policy enforcement happens at multiple layers:

  1. Connection Layer (src/gateway/server.ws-connection/): Device authentication, auth validation, pairing approval
  2. Channel Layer (src/channels/): Allowlist matching, DM/group policy, mention gates
  3. Command Layer (src/channels/command-gating.ts): Control command authorization
  4. Tool Layer: Tool-specific policy (ownerOnly, approval, sandbox restrictions)

This defense-in-depth approach means a bypass at one layer doesn't necessarily compromise the system—other enforcement points may still catch unauthorized actions.

Security Auditing

OpenClaw provides built-in tooling for auditing your deployment's security posture. The openclaw security audit command scans your configuration and reports potential issues.

Common Vulnerabilities

The security audit checks for these frequent problems:

CategoryFindingRiskRemediation
Exposuregateway.bind="0.0.0.0"Public network exposureBind to loopback or use firewall
Authgateway.auth.mode="none"No authenticationEnable token/password auth
Device AuthdangerouslyDisableDeviceAuth=trueBypassed identity verificationRemove flag, require pairing
Sandboxagents.defaults.sandbox.mode="off"Unrestricted tool executionEnable non-main or all
Toolstools.*.profile: "dangerous"Host filesystem accessUse messaging or stricter profiles

Security Testing

OpenClaw's test suite includes comprehensive security coverage:

# Run security-focused tests
pnpm test src/gateway/server.auth.*.test.ts
pnpm test src/gateway/server.device-*-authz.test.ts
pnpm test src/channels/*allow*.test.ts

# Run the security audit
openclaw security audit --deep

# Auto-fix where possible
openclaw security audit --fix

The audit command examines your actual running configuration, not just defaults—it reads ~/.openclaw/openclaw.json, active channel credentials, and current Gateway state.

CODEOWNERS Rules

The .github/CODEOWNERS file defines which teams must review changes to security-sensitive paths. This creates a mandatory security review gate before modifications can land:

// Security Audit Checklist (minimal implementation)

interface SecurityAuditChecklist {
  // Authentication & Authorization
  gatewayAuthMode?: string; // Should not be "none" for exposed deployments
  deviceAuthDisabled?: boolean; // Should be false
  bindAddress?: string; // Should be loopback for local-only
  
  // Allowlist & Policy
  dmPolicyOpen?: boolean; // Should use allowlist by default
  emptyAllowlists?: string[]; // Channels with no authorized senders
  
  // Sandbox & Tools
  sandboxEnabled?: boolean;
  dangerousToolProfiles?: string[]; // Tools with "dangerous" profile
  
  // Exposure
  publicBind?: boolean; // Bound to 0.0.0.0
  dangerouslyDisableDeviceAuth?: boolean;
}

function auditDeployment(config: SecurityAuditChecklist): string[] {
  const findings: string[] = [];
  
  if (config.gatewayAuthMode === "none") {
    findings.push("CRITICAL: Gateway authentication disabled");
  }
  
  if (config.dangerouslyDisableDeviceAuth) {
    findings.push("HIGH: Device auth bypassed");
  }
  
  if (config.publicBind && !config.deviceAuthDisabled) {
    findings.push("MEDIUM: Gateway exposed to network");
  }
  
  if (config.dmPolicyOpen) {
    findings.push("LOW: DM policy allows unlisted senders");
  }
  
  return findings;
}

🤿 深入底层:The security audit implementation lives in src/cli/commands/security-audit.ts. It reads your live configuration, evaluates against security baselines, and reports findings with severity levels. For --deep mode, it also checks running Gateway state via the WebSocket control protocol.

Security Review Process

When reporting security issues to OpenClaw, follow the process outlined in SECURITY.md:

  1. Report privately to [email protected] or the relevant GitHub repository
  2. Include all required information: title, severity, impact, reproduction steps, environment
  3. Demonstrate actual impact: Show how a documented trust boundary is bypassed
  4. Avoid false-positive patterns: Prompt-only injections, operator-intended features, and authorized-user actions are out of scope

The project maintains an explicit list of out-of-scope report types—including prompt-injection-only attacks (without boundary bypass), authorized local actions, and differences in heuristic detection between execution surfaces. These are hardening opportunities, not vulnerabilities.


Chapter Exercises

  1. Audit Your Deployment: Run openclaw security audit --deep on your current configuration. Review each finding and remediate critical/high issues. Document your security baseline.

  2. Implement Custom Allowlist Policies: Configure a channel with strict dmPolicy: "allowlist" and separate groupAllowFrom arrays. Test that unlisted senders cannot trigger your agent.

  3. Test Authentication Flows: Attempt to connect to your Gateway without valid credentials. Verify that device authentication is required and that the rejection is logged appropriately.


We've covered how OpenClaw's security architecture protects your local AI assistant—from authentication boundaries through policy enforcement to security auditing. These mechanisms work together to create a trusted local environment for running powerful AI capabilities.

But OpenClaw isn't just a tool you run—it's a community you can join. Let's now look at how to contribute to OpenClaw itself.