Harden authentication, API authorisation and response headers #4

Open
Otte wants to merge 1 commit from otte/security-hardening into main AGit
Contributor

multiple sec issues fixed.

  • forgot-password had no rate limit at all
  • password reset didn't kill API tokens or sessions
  • /api/team-products and /api/products/blueprint answered tokens with no
    abilities; team-products also returns drafts
  • published scope only checked status, isPublished() also checks published_at
  • no X-Frame-Options / nosniff / Referrer-Policy / Permissions-Policy
  • locale from /locale/{x} was stored unchecked
  • transfer API key compared with !=
  • every Discord link made a new row
  • connection edit form printed the raw data blob incl. OAuth tokens
  • release disk resolved three different ways
multiple sec issues fixed. - forgot-password had no rate limit at all - password reset didn't kill API tokens or sessions - /api/team-products and /api/products/blueprint answered tokens with no abilities; team-products also returns drafts - published scope only checked status, isPublished() also checks published_at - no X-Frame-Options / nosniff / Referrer-Policy / Permissions-Policy - locale from /locale/{x} was stored unchecked - transfer API key compared with != - every Discord link made a new row - connection edit form printed the raw data blob incl. OAuth tokens - release disk resolved three different ways
Harden authentication, API authorisation and response headers
Some checks failed
Tests / tests (pull_request) Has been cancelled
f517e5d52a
Addresses a set of low and medium severity issues found while reviewing
the application against a local instance.

Authentication and sessions:
- Throttle Fortify routes; /forgot-password previously accepted unlimited
  requests, allowing reset-token rotation and mail flooding.
- Revoke API tokens and database sessions when a password is reset, so a
  recovered account no longer leaves existing credentials valid.
- Make the Sanctum token lifetime configurable rather than fixed to never.

Authorisation:
- Require the read ability on /api/team-products and /api/products/blueprint.
  Both previously answered tokens created with no abilities at all, and
  team-products exposes unpublished drafts.
- Gate role assignment in the user resource behind the role update permission.
- Add impersonation guards so impersonating requires an explicit permission
  and administrators cannot be impersonated.

Input validation:
- Validate the locale against the available translations before storing it
  in the session.
- Compare the transfer API key with hash_equals and validate the payload
  instead of iterating unchecked request input.
- Look the transfer user up once instead of once per transaction.

Product visibility:
- Align the published scope with isPublished(). The scope matched on status
  alone, so products awaiting a publish date were listed publicly even though
  their detail page correctly returned 403.
- Set the status alongside published_at in the publish bulk action.

Transport and headers:
- Add X-Content-Type-Options, X-Frame-Options, Referrer-Policy,
  Permissions-Policy and, over HTTPS, Strict-Transport-Security.
- Default the allowed CORS origins to APP_URL instead of a wildcard.
- Make trusted proxies configurable, since the forwarded headers are enabled
  while no proxy is trusted.

Data handling:
- Store one connection per user and type, replacing the row created on every
  Discord link, and add the matching unique index.
- Stop rendering the raw connection payload in the admin form; it contains
  OAuth access and refresh tokens.
- Escape markup when encoding the product JSON-LD block.
- Resolve the product release disk in one place so uploads, downloads,
  deletions and size lookups agree.
Otte force-pushed otte/security-hardening from f517e5d52a
Some checks failed
Tests / tests (pull_request) Has been cancelled
to 972efe409d
Some checks are pending
Tests / tests (pull_request) Blocked by required conditions
2026-09-23 10:31:27 +00:00
Compare
Some checks are pending
Tests / tests (pull_request) Blocked by required conditions
This pull request can be merged automatically.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin +refs/pull/4/head:otte/security-hardening
git switch otte/security-hardening

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff otte/security-hardening
git switch otte/security-hardening
git rebase main
git switch main
git merge --ff-only otte/security-hardening
git switch otte/security-hardening
git rebase main
git switch main
git merge --no-ff otte/security-hardening
git switch main
git merge --squash otte/security-hardening
git switch main
git merge --ff-only otte/security-hardening
git switch main
git merge otte/security-hardening
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
ric-rac/sourcexchange.net!4
No description provided.