potiuk opened a new pull request, #3398:
URL: https://github.com/apache/apisix-dashboard/pull/3398
Note - this is really just a proposal for the APISIX PMC. this has been
found as a small inconsistency during preparation of the securiyt model, and
while not a CVE worthy security issue, but more of a
"hardening/defense-in-depth" and "consistency" change.
Kind request to consider applying this (or change it in the way that the PMC
would like to handle) - or ignore if you do not feel it's worth it - because
it does change user experience, so likely also some changelog documentation
might be needed if this change is accepted.
Happy to update it if more documentation/migration guides/changelog is
needed.
## Summary
Stops persisting the Apache APISIX admin key to `localStorage`
on the dashboard origin. The key is now held in-memory only;
operators re-enter it each browser session.
## The current behaviour
`src/stores/global.ts` defines:
```ts
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
// Admin key with persistent storage
export const adminKeyAtom = atomWithStorage<string>(
'settings:adminKey',
'',
undefined,
{
getOnInit: true,
}
);
```
With the 3rd argument left as `undefined`, jotai's
`atomWithStorage` defaults to `createJSONStorage(() => localStorage)`
([jotai docs](https://jotai.org/docs/utilities/storage#createjsonstorage)).
So the admin key is persisted to `localStorage` under the key
`settings:adminKey` and re-loaded on init (`getOnInit: true`).
## Why this is too broad
The admin key authorises **full Apache APISIX control-plane
mutation** — routes, plugins, consumers, SSL certs, every
write operation on the Admin API. The default behaviour widens
the credential's audience to anything that can read the
dashboard origin's `localStorage`:
- **XSS on the dashboard origin** — any reflected or stored XSS
on the dashboard reaches the admin key. The dashboard
renders user-supplied route names, plugin configs, consumer
identifiers, etc. — substantial untrusted-text surface.
- **Browser extensions** — extensions with `storage` permission
read `localStorage` cross-origin under certain configurations.
- **Shared workstations** — anyone with subsequent session
access to the same browser profile reads the key.
- **Filesystem access** — `localStorage` is stored in plaintext
in the browser profile directory.
The admin key is a single credential authorising every
mutation against the gateway control plane. Persisting it
broadly is inconsistent with how operators handle other
control-plane credentials (e.g. etcd auth, SSL cert private
keys, RBAC tokens — none of which are stored in browser
storage).
## The change
Replace `atomWithStorage` with `atom<string>('')`. The
exported `adminKeyAtom` is still an `Atom<string>` so
consumers (the existing `useAtom(adminKeyAtom)` callsites)
are unaffected at the type level. The behavioural difference
is that:
- The key is no longer persisted across page reloads.
- Operators re-enter the key each browser session.
- The `settings:adminKey` `localStorage` entry is no longer
written. (Existing entries are not cleared by this change —
see "Migration" below.)
## Migration
Existing dashboard users will lose the persisted key on first
page load after this lands. They re-enter the key once in the
Settings dialog. The previous behaviour can be restored on a
per-deployment basis via:
- Operator-supplied browser autofill / password manager —
reduces the persistence to a user-controlled credential
store rather than the dashboard origin's `localStorage`.
- Session-replay tooling for operations that genuinely need
session continuity across reloads.
Neither of those should be the default for an admin-key-class
credential.
The orphaned `localStorage` entry under `settings:adminKey`
is harmless after this lands (it's no longer read). A
follow-up could optionally clear it on first load for
hygiene, but that is not required for the security fix and
is out of scope here.
## Provenance
The behaviour was identified during a pre-flight security
audit conducted by the ASF Security team at the PMC's request,
documented in the `[email protected]` thread on 2026-05-17.
The PMC chair (Ming Wen) chose the "fix in dashboard first"
disposition on 2026-05-18 and on 2026-05-26 confirmed dropping
the dashboard from a separate scan-vendor pipeline pending
this fix.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]