Security Boundaries: Object Authorization, Input Validation, and Secret Handling
Enforce access on individual resources, keep data separate from executable structure, and prevent sensitive values from crossing logging and build boundaries.
On this page
Authorize the resource, not just the route
A logged-in user is not automatically allowed to read every object behind an authenticated endpoint. If /invoices/123 returns an invoice, the handler must check the caller's permission for invoice 123. A role such as "customer" is too broad unless it is combined with ownership or another resource-level rule.
Prefer a query scoped to the authenticated principal and tenant rather than loading any invoice and hoping the UI already filtered it. The principal must come from verified session context, not a request body. For administrative exceptions, make the policy explicit and test it separately. Deny by default when a permission or resource relationship is missing.
Validate shape and constrain authority
A schema checks that input has the expected fields and types. It does not establish that the caller may set every accepted field. Avoid mass assignment: accepting a valid role: "admin" string does not make it an authorized profile update. Define separate input schemas for distinct operations and allow-list the fields each operation can change.
Place limits on string length, array size, nesting, and uploaded bytes. Reject malformed input before expensive work. Keep query values separate from SQL structure using parameterized queries; select dynamic identifiers from a fixed allow-list. For HTML, use framework text rendering or context-appropriate encoding. Validation alone is not an XSS defense: a perfectly valid comment can contain angle brackets that must still be rendered as text.
Secrets have a lifecycle
Secrets must not appear in source control, client bundles, example code, or diagnostics. A variable placed in a server environment is only private while it stays on the server. Passing it as a component prop or using a public build-time prefix can expose it to every browser. Audit the actual output artifact, not just the source file where a value began.
Use narrowly scoped credentials, separate environments, and rotate credentials when exposure is suspected. Revocation must accompany rotation: creating a new key while leaving the leaked key active does not contain an incident. Logs and monitoring exports also need access control and retention limits; a redacted application log is not useful if a downstream tracing integration captures the same header unredacted.
Build an authorization test matrix
Test anonymous callers, valid owners, different owners in the same tenant, callers in another tenant, and administrators. Include read, update, delete, and bulk/export endpoints. Verify that an identifier supplied in a nested object does not bypass the same ownership rule.
For input boundaries, test unexpected fields, excessive collections, Unicode, and strings containing markup. Assert that displayed content remains text and that no input chooses a server-side function, query identifier, or filesystem path outside an allow-list. Finally, inspect logs from failed requests and confirm that passwords, session cookies, and authorization headers are absent. Security checks should be repeatable assertions, not a one-time manual inspection.