You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The reasoning in #35 is sound — MediaWiki never emits target= without title=, so the no-title shape is a reliable crawler fingerprint. But the mechanism being defended against is cache-busting via unbounded unique URLs, and adding &title=Main_Page to the same request restores the entire attack: still a unique URL per from= timestamp, still a full main-page render, still 0% cache hit rate, and now it sails past this check. The fingerprint was cheap to add and cheap to evade.
Related gaps in the same area:
Nothing constrains unknown or irrelevant query parameters generally. Any index.php?title=Main_Page&cachebust=<random> defeats CDN caching at zero cost to the attacker.
$wgCrawlerProtectedQueryParams matches on parameter presence only. There is no way to express "deny when limit exceeds N" or "deny when days exceeds N", which is where the actual cost lives — limit=5000&days=365 is expensive, limit=50&days=7 is not.
Proposals
Extend the check so a protected parameter combined with a title that does not name the special page which consumes that parameter is also denied (e.g. target= alongside title=Main_Page). This preserves legitimate title=Special:WhatLinksHere&target=Foo links while closing the trivial evasion.
Add value-threshold rules, e.g. $wgCrawlerProtectedQueryParamLimits = [ 'limit' => 500, 'days' => 30 ], denying anonymous requests that exceed them. This targets cost directly rather than shape, and would also mitigate the same attack arriving via a properly-titled URL.
Investigate the canonical-redirect idea raised at the end of Special-page query parameters sent without a title bypass CrawlerProtectedSpecialPages #35: when index.php receives parameters that cannot affect the resolved output, issue a 301 to the canonical URL. This collapses the cache-busting entirely and is cheaper than either a render or a denial. It is a larger change and arguably belongs upstream, but it addresses the mechanism rather than one signature and is worth prototyping here first.
Whichever approach is chosen, ship it behind config that defaults to today's behaviour, and add the dry-run/logging mode requested in the observability issue so operators can measure false positives before enforcing.
Requirement type: functional
Problem
The
$wgCrawlerProtectedQueryParamsmechanism added in response to #35 denies a request only when the protected parameter appears without atitle:The reasoning in #35 is sound — MediaWiki never emits
target=withouttitle=, so the no-title shape is a reliable crawler fingerprint. But the mechanism being defended against is cache-busting via unbounded unique URLs, and adding&title=Main_Pageto the same request restores the entire attack: still a unique URL perfrom=timestamp, still a full main-page render, still 0% cache hit rate, and now it sails past this check. The fingerprint was cheap to add and cheap to evade.Related gaps in the same area:
index.php?title=Main_Page&cachebust=<random>defeats CDN caching at zero cost to the attacker.$wgCrawlerProtectedQueryParamsmatches on parameter presence only. There is no way to express "deny whenlimitexceeds N" or "deny whendaysexceeds N", which is where the actual cost lives —limit=5000&days=365is expensive,limit=50&days=7is not.Proposals
titlethat does not name the special page which consumes that parameter is also denied (e.g.target=alongsidetitle=Main_Page). This preserves legitimatetitle=Special:WhatLinksHere&target=Foolinks while closing the trivial evasion.$wgCrawlerProtectedQueryParamLimits = [ 'limit' => 500, 'days' => 30 ], denying anonymous requests that exceed them. This targets cost directly rather than shape, and would also mitigate the same attack arriving via a properly-titled URL.index.phpreceives parameters that cannot affect the resolved output, issue a 301 to the canonical URL. This collapses the cache-busting entirely and is cheaper than either a render or a denial. It is a larger change and arguably belongs upstream, but it addresses the mechanism rather than one signature and is worth prototyping here first.