Skip to content

Security: use a high-entropy random key for the security-mode disable URL#1382

Open
vuckro wants to merge 2 commits into
Ultimate-Multisite:mainfrom
vuckro:security/sunrise-security-mode-key
Open

Security: use a high-entropy random key for the security-mode disable URL#1382
vuckro wants to merge 2 commits into
Ultimate-Multisite:mainfrom
vuckro:security/sunrise-security-mode-key

Conversation

@vuckro

@vuckro vuckro commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

The unauthenticated ?wu_secure=KEY query string that turns the network-wide
recovery security mode off used substr(md5(admin_email), 0, 6) as the key
— only ~24 bits and derived from a commonly public/guessable value. An attacker
could compute or brute-force it and remotely disable the admin's safe-mode
lockdown (re-enabling all other plugins).

Changes

  • Generate a 128-bit random key once (random_bytes — this runs from sunrise,
    before pluggable.php) and store it as a network option.
  • Compare it with hash_equals().

The key is already displayed on the Settings screen ("copy this URL to disable
security mode"), so the documented recovery workflow is unaffected — the
settings note now shows the new random URL.

Summary by CodeRabbit

  • Bug Fixes
    • Improved security mode protection through enhanced cryptographic verification and random secret generation to prevent timing and brute-force attacks.

The unauthenticated ?wu_secure=KEY query string that turns the network-wide
recovery "security mode" off used substr(md5(admin_email), 0, 6) as the key —
only ~24 bits and derived from a commonly public/guessable value, so an attacker
could compute or brute-force it and remotely disable the admin's safe-mode
lockdown.

Generate a 128-bit random key once (random_bytes, since this runs from sunrise
before pluggable.php) and store it as a network option, and compare it with
hash_equals(). The key is already displayed on the settings screen, so the
documented "copy this URL to disable security mode" workflow is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@superdav42, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 14 minutes and 43 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dc216be3-292c-4f28-93db-ba3db38db803

📥 Commits

Reviewing files that changed from the base of the PR and between 708d48c and 1580eaf.

📒 Files selected for processing (3)
  • inc/class-sunrise.php
  • inc/functions/sunrise.php
  • tests/WP_Ultimo/Functions/Sunrise_Functions_Test.php
📝 Walkthrough

Walkthrough

The pull request hardens security mode authentication in the sunrise.php initialization flow. A deterministic key derivation is replaced with high-entropy random secret generation and persistence, and request validation switches to a timing-safe comparison function to prevent timing attacks.

Changes

Security mode authentication hardening

Layer / File(s) Summary
Secure random key generation with persistence
inc/functions/sunrise.php
wu_get_security_mode_key() generates a 16-byte random secret via random_bytes and bin2hex, stores it in network option wu_security_mode_key on first call, and returns the persisted value thereafter, replacing deterministic md5(admin_email) truncation.
Timing-safe comparison with hash_equals
inc/class-sunrise.php
Sunrise::load() security-mode validation switches from direct equality comparison to hash_equals() for constant-time comparison of the incoming wu_secure query parameter against the key from wu_get_security_mode_key().

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A secret key now hides so deep,
Stored safely where the options sleep,
No timing leaks shall slip away,
With hash_equals() here to stay! 🔐

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main security improvement: replacing a predictable email-hash-derived key with a high-entropy random key for the security-mode disable URL.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@inc/class-sunrise.php`:
- Line 338: The code currently casts wu_get_isset($_GET, 'wu_secure') to string
and passes it to hash_equals, which can trigger "Array to string conversion" for
inputs like ?wu_secure[]=x; update the check in the sunrise bootstrap (around
the hash_equals call) to first retrieve the value via wu_get_isset($_GET,
'wu_secure'), verify it is a string (e.g. is_string(...) or ensure it's scalar
and specifically a string), and only then call
hash_equals(wu_get_security_mode_key(), $value). If the value is not a string,
skip the hash_equals branch (treat as non-matching) to avoid warnings and
potential log spam. Ensure you reference the wu_get_isset call, the 'wu_secure'
GET key, and the hash_equals/wu_get_security_mode_key invocation when
implementing the guard.

In `@inc/functions/sunrise.php`:
- Around line 91-96: The code in Sunrise::load() currently auto-generates and
persists a new wu_security_mode_key on first read (using get_network_option,
random_bytes, bin2hex, update_network_option), which silently invalidates
pre-issued recovery URLs; instead, stop creating a new random key during
ordinary reads — if wu_security_mode_key is missing, temporarily accept the
legacy/derived key (compute the legacy value the same way the Settings screen
previously derived it) until an admin explicitly sets or a migration persists a
new random key; move the update_network_option(random key) call out of
Sunrise::load() into an explicit migration/upgrade routine or the settings save
path so the secret is only rotated when persisted intentionally, and ensure
load() checks both the persisted option and the legacy-derived key for
validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5778e68c-3587-413b-b8e2-f685b86e88c3

📥 Commits

Reviewing files that changed from the base of the PR and between 1310078 and 708d48c.

📒 Files selected for processing (2)
  • inc/class-sunrise.php
  • inc/functions/sunrise.php

Comment thread inc/class-sunrise.php Outdated
Comment thread inc/functions/sunrise.php
@superdav42

Copy link
Copy Markdown
Collaborator

CLAIM_RELEASED reason=worker_complete runner=superdav42 ts=2026-06-10T13:55:22Z aidevops_version=3.20.46 opencode_version=1.16.2

@superdav42 superdav42 added the status:available Task is available for claiming label Jun 10, 2026
@superdav42

Copy link
Copy Markdown
Collaborator

Permission check failed for this PR (HTTP unknown from collaborator permission API). Unable to determine if @vuckro is a maintainer or external contributor. A maintainer must review and merge this PR manually. This is a fail-closed safety measure — the pulse will not auto-merge until the permission API succeeds.


aidevops.sh v3.20.52 plugin for OpenCode v1.17.3 with gpt-5.5 spent 2m and 47,696 tokens on this as a headless worker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contributor status:available Task is available for claiming

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants