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]

Reply via email to