Secure the calendar iCal feed against unauthenticated disclosure#975
Open
GaryJones wants to merge 1 commit into
Open
Secure the calendar iCal feed against unauthenticated disclosure#975GaryJones wants to merge 1 commit into
GaryJones wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The calendar's
.icssubscription feed is served from anoprivAJAX handler, so it is reachable without logging in. Its only gate wasuser_key = md5( user_login . <single site-wide secret> ), compared in non-constant time, and the feed query was never scoped to the named user — it even honoured caller-suppliedauthor/post_statusfilters. Because admin-ajax runs withis_admin()true, anyone holding a feed URL could read other authors' unpublished posts, and a single leaked URL (or knowledge of the shared secret) compromised every user's feed.This reworks the feed around a per-user, revocable secret stored in user meta. The handler resolves the supplied username to a real user, requires that user can view the calendar, and validates the secret with
hash_equals()— run unconditionally against a real-or-dummy value to limit username enumeration via timing. It then runs the feed as the resolved user and restricts the query to that user's own posts unless they can edit others' (mirroring the core posts list), so a leaked URL can no longer disclose other authors' work. Caller-suppliedauthor/post_statusfilters are ignored on this context, and the "Regenerate calendar feed secret" action now rotates only the current user's token rather than everyone's.This is a deliberate hard cutover: old
md5(site-secret)URLs stop working, and each user re-copies their new URL from Screen Options on the Calendar (it is minted on first view). That is the point — the old, broadly-guessable URLs are invalidated.Fixes VIPPLUG-4.
Test plan
composer test:integration -- --filter CalendarIcsSubscriptionAjaxTestpasses (9 tests): valid token returns the user's own feed; a leaked URL does not expose another author's unpublished posts; an editor still sees the whole pipeline; invalid, legacymd5(site-secret), and unknown-user requests are rejected; per-user secrets are isolated; and callerauthor/post_statusfilters are ignored. It also asserts thenoprivhook is registered for logged-out users, guarding the structural root cause.--filter 'CalendarModule|CalendarMetadataAjax'and theCalendarunit class): 29 tests green; the dashboard query path is untouched (the new scoping is gated on the subscription context).