Threat Model: Admin Pages
STRIDE review of admin page loads, page guards, layout data and the administration landing route.
On this page
Scope: src/routes/(app)/+layout.server.ts, +layout.ts, every page below
src/routes/(app)/config/**, (app)/dashboard/**, (app)/plugin/**,
src/routes/(admin)/** and the shared guards in src/utils/page-guards.server.ts.
The admin layout owns the session-aware page payload; the authorization hook
(src/hooks/handle-authorization.ts) decides whether the request reaches it.
STRIDE
| Threat | Surface | Existing control (file reference) | Residual risk |
|---|---|---|---|
| Spoofing — unauthenticated visitor opens an admin page | src/utils/page-guards.server.ts |
getAuthenticatedUser() throws a redirect to the login page when locals.user is absent; requirePagePermission() fails closed on a missing permission. |
A page that reads locals.user directly instead of the guard can render a partial shell; code review must reject that pattern. |
| Spoofing — tenant-bound admin reaches a system-only page | src/routes/(admin)/admin/+page.server.ts |
The landing route requires locals.isAdmin and rejects users with a tenantId via a redirect. |
The same distinction must be repeated in any new system-only route; this model covers the existing route only. |
| Tampering — admin writes bypass the API authorization path | src/routes/(app)/+layout.server.ts, src/hooks/handle-authorization.ts |
Page loads are read-only; mutations go through /api handlers that run _checkEndpointPermission(). |
A form action that writes directly to an adapter would bypass the endpoint mapping and must be rejected in review. |
| Repudiation — admin change without an audit entry | src/hooks/handle-audit-logging.ts |
The audit hook wraps authenticated admin mutations in the server pipeline. | Page loads themselves are not mutations and are not audited. |
| Information disclosure — layout data leaks another tenant’s config | src/routes/(app)/+layout.server.ts |
Layout data is resolved per request from the authenticated user’s tenant scope. | Cached settings must be keyed by tenant; a global cache key is a defect this model expects tests to catch. |
| Denial of service — expensive dashboard widgets | src/routes/(app)/dashboard/+page.server.ts |
Widget data is loaded server-side; expensive system metrics are served by dedicated API handlers with their own rate lane. | A dashboard with many widgets still issues several queries per load; pool sizing is operational. |
| Elevation of privilege — plugin admin view renders privileged actions | src/routes/(app)/plugin/[...path]/+page.server.ts |
The plugin page resolves the plugin and the current user before rendering; API calls from the view are still gated by the dispatcher. | Plugin view code is third-party and can render any UI; the API gate remains the enforcement point. |
Assumptions
- The
(app)layout is the only place that assembles the admin page payload; individual pages add data, not identity. - The authorization hook runs before every
(app)and(admin)request, including client-side navigations that hit a server load. - Security headers (CSP, HSTS,
X-Frame-Options) are applied globally bysrc/hooks/handle-security-headers.ts.
Was this page helpful?