Skip to content

Security: Gateway auth token exposed in /api/config response #45

@iAhmadZain

Description

@iAhmadZain

Summary

The /api/config API route returns the gateway authentication token in its JSON response, making it accessible to any user who can reach the dashboard.

Location

app/api/config/route.ts — near the end of the GET handler:

const data = {
  agents: agentsWithStatus,
  providers,
  defaults: { model: defaultModel, fallbacks },
  gateway: {
    port: config.gateway?.port || 18789,
    token: config.gateway?.auth?.token || "",  // ⚠️ leaks token
    host: config.gateway?.host || config.gateway?.hostname || "",
  },
  groupChats,
};

Impact

  • Anyone with access to the dashboard (even read-only) can extract the gateway auth token
  • The token grants full control over the OpenClaw gateway (config changes, restarts, session access)
  • Especially dangerous if the dashboard is exposed via reverse proxy or tunnel without additional authentication

Proposed Fix

Option A (minimal): Redact the token, expose only a boolean for health check purposes:

gateway: {
  port: config.gateway?.port || 18789,
  hasToken: !!(config.gateway?.auth?.token),
  host: config.gateway?.host || config.gateway?.hostname || "",
},

Then update gateway-health/route.ts to read the token directly from config on the server side (it already does this) rather than receiving it from the client.

Option B (if token is needed for client-side gateway probes): Pass a short-lived HMAC or session-scoped proxy token instead of the raw gateway credential.

Additional Recommendation

Consider adding authentication middleware to the dashboard itself (even a simple env-based password check) since it exposes sensitive operational data including:

  • Agent configurations and models
  • Session statistics and token usage
  • Platform binding details
  • Gateway connectivity status

A lightweight approach:

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const token = request.headers.get("authorization")?.replace("Bearer ", "");
  if (token !== process.env.DASHBOARD_TOKEN) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
}

export const config = { matcher: "/api/:path*" };

Great project overall — just needs this credential exposure patched before production/public use. Happy to submit a PR if preferred.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions