
No. 16 · 30 October 2026 · Updated 1 November 2026 · Security · Web
HTTP method equivalence is not state equivalence
On 4.1% of top sites a POST could become a GET and get the same answer back. The same answer isn't the same effect. What the figure measures, the defense gap the paper did find, and how to test your own endpoints.
In plain terms: each time your browser asks a website for something, the request carries a method, a verb that says what kind of request it is. The two common ones are GET, which "should only retrieve data," and POST, which sends something to the site, "often causing a change in state" there (Mozilla's descriptions). Websites are supposed to keep the two apart, and some security protections rely on that.

A 2026 paper by Matteo Golinelli, Seif Errahmane Hannachi and Bruno Crispo, "When POST Becomes GET," built a crawler, MIDE, that recorded POST requests on popular websites, resent them as GET requests, and checked whether the same answer came back1. On 407 of the 10,000 sites it covered, 4.1 percent, at least one request did, by the paper's strict test. This essay asks what that figure does and doesn't show. Getting the same page back is not proof the site did the same thing behind the scenes.
Why it matters to you: several of the protections that keep other websites from acting in your name treat POST as dangerous and GET as harmless. When a site will act on a GET as readily as on a POST, another website can get your browser to send that request with your login attached, and change something on your account. That attack is cross-site request forgery, or CSRF.
The paper's numbers depend on what you divide by. 407 sites is 4.1 percent of the 10,000 tested, 12.7 percent of the 3,200 where the crawler captured any POST at all, and 14.9 percent of the 2,734 where a POST could be replayed 1. The paper also ran two security checks that go past response matching. It replayed 115 requests that carried anti-forgery tokens, and found 7, on 2 sites, where a forged token was refused on POST but accepted on the equivalent GET. And it found dynamic pages that a shared cache would hand to a stranger 1. Neither check showed an account actually being changed or private data actually leaking, and the authors say so. This essay keeps those lines where they drew them, explains why response equivalence is the wrong oracle for both kinds of risk, and proposes tests that measure state and cache exposure directly.
Corrections. The version of this essay published on 30 October 2026 said the paper found the frameworks of the 407 sites matched the wider population. The paper reports no such comparison. That version also proposed a state test that compared two requests run one after the other, rather than from the same starting point. It treated a cache header as a cache-deception finding, and it left out the paper's token experiment. Revision 2, on 1 November, corrects all four, and each correction is marked where it happens. Revision 3 tightened the proposed experiments.
- 4.1% is not a risk rate. It counts sites where a GET got the same answer as a POST. It doesn't count sites where the GET did the same thing 1.
- The denominator matters. Among sites where the crawler could replay a POST, the rate is 14.9% 1.
- The paper did find a defense gap. On 7 requests across 2 sites, a forged anti-forgery token was refused on POST and accepted on GET. That is a method-scoped gap, not a confirmed exploit 1.
- Read-only is not safe. A page that changes nothing can still leak through a shared cache 18.
- Measure effects, not answers. Compare what each method does from the same starting state, and test cache exposure across sessions. A header is a hint, not a finding.
- Security teams: A matching response tells you nothing about what the request changed. Test the effect.
- Developers: Some frameworks route every method to a handler unless told otherwise. Check what yours does with a GET to a POST-only action 910.
- Auditors: Ask whether CSRF defenses run on every method that reaches an action, not only on POST 13.
- Researchers: Quote 4.1% with its denominator and its definition, or don't quote it.
This is my essay. I chose the question, framed the hypotheses and the tests, decided which guesses were educated and which were just guesses, set what counts as a primary source here, and checked the facts against the paper. Then I sent it back, more than once.
I also lean on a lot of gonzo machinery, and you should know what it does. Drafts go through the Moot before I read them: a panel of frontier models I call by their lab bosses' first names (Dario, Sam, Sundar), plus Eddie, the house model that drafts and rules on their objections. Once the material met my standard, it was put into my voice from a written guide to how I talk, and a model from a different lab checked that the voicing hadn't moved the facts; it flagged 1 of 148 claims, which is a consistency check, not a measure of accuracy. Gemini drew the plates, after Sempé, to my direction.
After the first version went up on 30 October, three outside readers (Claude, Gemini and OpenAI's Astra) sent critiques. I decided what to take. Claude drafted this revision against the paper itself, with every number checked against its tables, and nothing went out until it met my standard. Where a reviewer and the paper disagreed, the paper won.

The 4.1% comes from a measurement of the Tranco top 10,000 websites, a standard research list of the most visited sites 1. The crawler, MIDE (Method Interchange Detection Engine), loaded each site in a real browser, recorded the POST requests that page loads and button clicks produced, moved each POST's parameters into a URL as a GET, and compared the answers 1. It filled forms with harmless made-up values, and it dropped any request whose address matched destructive or session-changing patterns, logout included, along with file uploads 1. By design, it was a crawler that declined to press the scary buttons. It also never logged in: the whole crawl ran without an account 1.
So the 4.1% is a lower bound on interchangeability, not a confirmed vulnerability rate. It's a lower bound because the riskiest buttons were never pressed and no logged-in page was ever seen.
Here is the funnel, with each rate against its own denominator 1:
| Population | Interchangeable | Rate |
|---|---|---|
| All sites tested | 407 of 10,000 | 4.1% |
| Sites with a captured POST | 407 of 3,200 | 12.7% |
| Sites with a replayable POST | 407 of 2,734 | 14.9% |
| Replayed routes | 646 of 8,291 | 7.8% |
| Replayed requests | 1,228 of 14,244 | 8.6% |
"Interchangeable" here means the paper's high-confidence Class A: the converted GET matched the POST's answer, and a control GET with no parameters did not 1. That control matters. Another 1,782 requests matched too, but so did the bare control, which means the page was probably the same whatever you sent; the paper set those aside as ambiguous (Class B) 1. Sites, routes and requests are three different units, and each line of the table has its own denominator.
The paper sorts the 1,228 Class A requests with a rough taxonomy: 379 (30.9%) search or filtering, 186 (15.1%) analytics or tracking, 121 (9.9%) API calls, 109 (8.9%) authentication, and 94 (7.7%) unknown 1. It calls search, analytics, contact forms and newsletters likely lower-sensitivity, and warns that the grouping is coarse. A search endpoint whose results depend on who is logged in can leak through a side channel called XS-Search, which an unauthenticated crawl can't see 1.

Two men raise their hats to each other across a square in exactly the same way, which tells you nothing about what either is carrying. Response equivalence means the server returns the same answer to two methods. State equivalence means it does the same thing for both:
the same database write, the same email sent, the same setting changed. The 4.1% measures the first and not the second. A server can answer a GET and a POST the same way while the POST triggers a database write that the GET doesn't. The answer is interchangeable; the effect isn't.
The raw crawl data is held by the authors and won't be released; the public artifact has the code and synthetic examples 1. Nobody outside can yet count how many of the 1,228 requests change anything. Quoted as a CSRF rate, the 4.1% turns into a mumpsimus, still repeated long after the paper said otherwise.
Response equivalence is the wrong oracle for both risks this paper touches. CSRF needs an effect, and cache deception needs exposure. A matching answer establishes neither.
Why GET matters at all. Any website can make your browser send a request to another one: a link, an image tag, a redirect, or a hidden form that posts itself. What differs is whether your login goes with it. Under the common cookie setting SameSite=Lax, cookies ride along on a cross-site top-level GET navigation, but not on a cross-site POST 1. And CSRF tokens, the secret values a site embeds in its forms to prove a request came from its own page, are usually checked only on unsafe methods 13. So an action that works over GET can slip past both defenses at once. That is the reason method interchangeability is a security question rather than a style question.

What the paper tested. It went past matching answers. From the Class A requests it took the 115 whose POST carried a token-like field (csrf, xsrf, authenticity_token and the like), across 46 sites, and replayed each one with the token removed and with it changed, as
POST and as GET 1. Picture the clerk with the magnifying glass, studying every envelope at the slot while a large parcel sits on the floor behind him, unexamined. In 7 cases, on 2 sites, the POST refused a bad token and the GET accepted it 1. Another 78, across 25 sites, accepted bad tokens either way: no enforcement was observed for either method in those runs, so no method gap either. Of the rest, 3 were mixed with no GET-specific difference, 16 had a POST that failed even with the real token, 1 checked the token on GET, and 10 were inconclusive, for 115 in all 1. The authors did not check whether those 7 requests changed anything, whether they carried user-specific data, or whether another site could trigger them, so they call them evidence of method-scoped defense gaps, not confirmed CSRF exploits 1. That is exactly the right size of claim. The 30 October version of this essay left the experiment out entirely.
What the paper found about caches. Think of a cloakroom that hands your checked overcoat to the next man in line because his ticket looked close enough. Web cache deception (WCD) is that attack: a shared cache stores a page meant for one logged-in user under an address it thinks is a static file, then serves it to whoever asks next 8. It needs no state change at all. The paper tested each of the 1,228 requests with 12 deception payloads, populated the cache once, and fetched again from a separate session without the original credentials 1. Its findings come in stages:
| Observation | Established by the study? |
|---|---|
| GET and POST answers interchangeable, high confidence | Yes: 1,228 requests on 407 sites |
| Token check enforced on POST but not on GET | Yes: 7 requests on 2 sites |
| Deception payload produced a cross-session cache hit | Yes: 56 requests on 9 sites |
| Converted GET already cacheable without a payload | Yes: 136 requests on 50 sites |
| An account changed by a forged cross-site request | Not established |
| Private data disclosed to someone else through a cache | Not established |
The 30 October version described the 56 as "WCD consequences." They are payload- induced cache hits on dynamic pages. The crawl never logged in, so it couldn't show anyone's account data leaking, and the paper doesn't claim any did 1.
What "safe" means. RFC 9110, the standard that defines HTTP, calls GET a safe method. Safe means the client doesn't request a state change and isn't responsible for one. It doesn't mean nothing happens: incidental effects such as logging are explicitly allowed 2. A GET that writes an access log is compliant. A GET that sends money isn't. So detecting a database write doesn't by itself prove an HTTP safety violation; what matters is whether the request asked for the effect.

Django builds on that reading. It skips CSRF token checks for safe methods by design 3. If a GET in a specific Django application is configured to perform a state change, the CSRF token check is still skipped, and that creates a vulnerability 3. The framework kept its promise; the application broke the assumption.
Whether a POST can become a GET depends on two separate things: which methods a route accepts, and where the handler reads its inputs from 1. The paper calls these route reachability and parameter-source abstraction, and frameworks differ on both.
In Flask, a route is GET-only unless the developer lists more 7. More precisely, a default Flask route supports GET, automatically supports HEAD, and normally supplies OPTIONS; other unconfigured methods receive 405, the "Method Not Allowed" error 7. Add methods=['GET', 'POST'] and both work.
Django keeps query parameters in request.GET and form data in request.POST, two separate collections; reading both takes deliberate code 11. A view can still accept several methods, and one that handles GET and POST the same way is interchangeable 1.
Spring's @RequestMapping "by default, matches to all HTTP methods," in the documentation's words; its shortcut annotations such as @PostMapping exist so that most handlers are tied to one method 9. Omit the attribute and GET, POST, PUT, DELETE, and PATCH are all accepted.
PHP's $_REQUEST by default contains the contents of $_GET, $_POST and $_COOKIE; the request_order setting changes what goes in 10. It decides nothing about which methods reach the script. Code that reads $_REQUEST can't tell whether a value arrived in the URL or the body, which is the whole problem in one variable.
The paper sorts the frameworks it audited along those two axes 1. Vanilla PHP, and some Spring MVC and ASP.NET Core configurations, combine permissive routing with merged inputs, and are the most prone. Laravel, Flask, Rails, Sinatra and Gin get there only if a developer adds multi-method routes. Django, Express, FastAPI, NestJS, Next.js and SvelteKit keep their input sources more explicit.
That is a map of mechanisms, not a census. The paper doesn't fingerprint the frameworks behind the 407 sites or compare them with the wider list 1. The 30 October version said it did, and that the distributions matched. That sentence had no source, and it's gone. Which frameworks actually produce the positives in the field is an open question, set out as H3 below.

A web application firewall (WAF) is a filter in front of a site that blocks requests that look like attacks. If converting POST to GET moved an attack past the firewall, interchangeability would matter even more. For the standard ruleset, the source says otherwise. The OWASP Core Rule Set (CRS) 4.0 rule 942100 targets the ARGS collection, covering both query strings and body parameters 6. A SQL injection payload gets the same rule whichever way it arrives, provided the deployment parses the body 6.
The bypasses reported in ModSecurity issues #3585 (GET) and #3587 (POST) don't change that 45. They concern SQLi payload obfuscation on CRS 3.3.2, not POST-to-GET method conversion, and the maintainer noted that 3.3.2 is end-of-life 5. They care how the payload is spelled, not how it was mailed.
Matching rule targets don't guarantee matching behavior at run time, though. Body parsers, content types, normalization and local exclusions can all differ between a query string and a body. Whether they do in a given deployment is an experiment, H2 below, and a small one; it supports this essay's argument rather than being a second paper inside it.
Keep unsafe actions on unsafe methods, and run CSRF defenses on every method that reaches them. A login page may serve its form over GET while processing credentials through a protected POST handler. Restricting the action's method does not replace CSRF protection or controls against caching sensitive responses. Accepts GET? Then it shouldn't act.

To test whether two methods do the same thing, don't run one after the other. The 30 October version did exactly that: POST, snapshot, GET, snapshot, compare. Imagine an endpoint that transfers ten dollars on every call. POST moves ten, GET moves ten more, the snapshots differ, and the test reports "not interchangeable" when both methods did the same unsafe thing. It's walking across the snowfield twice and blaming the second set of footprints for the first.
The test that answers the question runs each method once, from the same starting point:
- Restore a snapshot S ₀. Send the POST, as an authenticated user, and record everything that changed: database rows, files, queues, outbound calls, session state. That's Δ POST.
- Restore the same S ₀, with the same user and the same authorization. Send the converted GET and record Δ GET.
- Set aside the incidental effects RFC 9110 allows, such as access logs, analytics counters and cache fills, and compare what's left. Judge each effect by what it does, not by its name: a last-seen timestamp that only decorates a profile is incidental, but one that keeps a session alive isn't.
Look at both deltas before calling anything. A GET that acts when the POST doesn't is the most surprising result of all, and worth its own line. If Δ POST and Δ GET match and aren't empty, the GET can do what the POST does. That's a finding about state, which is the question the 4.1% couldn't answer. It isn't yet a CSRF finding: that also needs a browser that will send the request cross-site with the victim's credentials, no defense that stops it, and an effect the user didn't intend.
The obvious place to start is the authors' own artifact, which includes a local Flask target for crawling and POST-to-GET replay that runs under Docker Compose 1. It's where the two-arm test could be built and checked before it goes near anyone's real site.
Cache exposure gets its own test, and a header doesn't settle it. An ETag is a validator for a page; it doesn't mean a shared cache stored anything. Cache-Control: public invites caching but doesn't prove it happened. And classic cache deception often works with neither, through a path that the cache reads as a static file and the server reads as a dynamic page 8. Record three outcomes, as the paper effectively did: a cacheability signal, a cross-session cache hit, and an unauthorized retrieval of private content 1. Only the last is a disclosure.
- H1 (Most interchangeable requests don't act): Among the paper's 1,228 Class A requests, fewer than 20% let the converted GET execute a client-requested unsafe effect: a committed change to stored data, account or session settings, or an external action, measured with the two-arm test from section 5 and judged by meaning, not by column name. That GET outcome is the primary one. Whether the POST acts, and whether both do, are reported beside it.
- Falsifier: Sample sites, not requests, since requests on one site share code: draw at least 60 of the 407, test their requests with weights that restore the original request-level population, and use cluster-aware confidence intervals, reporting site, route and request counts separately. Test only on authorized replicas or with site owners' permission. A sampled request that can't be tested is reported as missing, never swapped for a convenient one, and if more than a fifth are missing, the result describes the tested sites only. If the 95% upper confidence bound on the unsafe share is below 20%, H1 is supported; if the lower bound is above 20%, it's falsified; otherwise it's inconclusive. The raw crawl is author-held, so this needs the authors1.
- H2 (WAF parity): In a pinned ModSecurity and CRS 4.x deployment, with explicit parsers for URL-encoded and JSON bodies, paired SQL injection payloads are blocked at the same rate in a GET query string as in a POST body, within 5 percentage points.
- Falsifier: Use one payload corpus, each payload sent both ways, with known- benign and known-malicious controls in every run. The interval is computed on the
pairs, since each payload appears in both methods, and URL-encoded and JSON bodies are reported separately rather than pooled. If the 95% confidence interval for the paired difference lies inside ±5 points, H2 is supported. If it lies outside, H2 is falsified. Otherwise it's inconclusive.
- H3 (Framework prevalence): Among the 407 sites, those identifiable as vanilla PHP or Spring MVC are at least 1.5 times as common as in the identifiable Tranco 10,000 baseline. This is untested; the paper doesn't report it 1.
- Falsifier: Measure two things separately: the framework, with a fingerprinter validated against sites of known framework and its misclassification rate reported; and the deployed behavior, whether the route actually accepts both methods and binds the same inputs, which no framework label can tell you. Compare against the baseline with the 407 removed from it, since they're part of the 10,000. If the 95% interval for the framework ratio lies above 1.5, H3 is supported; below 1.5, falsified; otherwise inconclusive. Then report whether interchangeability tracks the framework, the route configuration, or both. Either way it's an observational result: it can show over-representation, not cause.
- H4 (Read-only pages can leak through caches): At least one of the paper's 56 payload-induced cache hits serves one logged-in user's private content to a separate session without credentials, when re-run with a consenting test account.
- Falsifier: With permission from the 9 sites, re-run every one of the 56 cases with a logged-in test account. If none of the cases actually tested serves user-specific content across sessions, H4 is falsified for those cases only. Cases a site declines, or that can't be reproduced, are reported as untested, never counted as negatives, so partial consent gives a partial result, labeled as one. A single confirmed case supports H4. The 30 October version compared read-only and state-changing risk overall; that comparison had no common measure and is withdrawn.
Want to know if a GET can do what a POST does? Run each from the same starting point. Want to know if a page can leak through a cache? Fetch it from a second session. Want to know if a defense covers both methods? Forge the token and try both.
The sketch below is conceptual: a sketch, not runnable, with helpers such as effects and requested_only left undefined. Every step runs only on systems you're authorized to test.
// 1. Same effect? Two arms, one starting state.
function state_effects(post, get, user):
restore(S0)
d_post = effects(send(post, as=user))
restore(S0)
d_get = effects(send(get, as=user))
// keep effects the request asked for; judge
// each by what it does, not its column name
p = requested_only(d_post)
g = requested_only(d_get)
if g and not p: return "GET-only unsafe effect"
if g and g == p: return "GET does what POST does"
if g: return "both act, differently"
if p: return "only POST acts"
return "neither acts"
// 2. Same defense? Forge the token both ways.
function token_gap(post, get):
p = send(post with token changed)
g = send(get with token changed)
if rejected(p) and accepted(g):
return "method-scoped token gap"
// still not an exploit: check the effect,
// cross-site sending, and user intent
return "no GET-specific gap"
// 3. Cache exposure: try every variant and
// keep the strongest outcome, with evidence.
function cache_exposure(get_url, victim):
hits = 0; leaks = 0
for url in deception_variants(get_url):
send(url, as=victim) // fill cache
r = send(url, as=nobody) // new session
record(url, r)
if cache_hit(r):
hits = hits + 1
if private_to(r, victim): leaks = leaks + 1
if leaks: return "unauthorized disclosure"
if hits: return "cross-session cache hit"
if cacheable_hint(get_url):
return "cacheability signal"
return "none"| Claim | How to check |
|---|---|
| "4.1% of top sites are CSRF-vulnerable." | It's 407 of 10,000 sites with matching answers, from a logged-out crawl that skipped destructive buttons [1]. Ask for the effect test. |
| "GET requests are always safe." | RFC 9110 §9.2.1: safe means no client-requested unsafe action; incidental logging is allowed [2]. Check the handler, not the verb. |
| "WAFs ignore query strings." | CRS 4.0 rule 942100 targets ARGS, query and body alike [6]. Run paired payloads before believing either way. |
| "It's cacheable, so it's cache deception." | A header is a hint. Ask for a cross-session cache hit, and then for private content in it [1][8]. |
| "We compared the database before and after." | Ask: before and after what? Two methods run back to back measure the second on top of the first. |
| "Read-only endpoints are safe." | Cache deception needs no state change [8]. Test exposure separately. |
- One paper, version 1: Every measured number here comes from the authors' v1 preprint, posted on 27 October 2026 and not yet peer reviewed. We've checked our figures against its tables, but we haven't run its crawler, and we haven't asked the authors to check our reading.
- Logged out: The crawl never authenticated 1. Pages behind a login, where most account-changing actions live, are unmeasured, by the paper and by us.
- Skipped buttons: The crawler avoided destructive and session-changing controls by design 1. The riskiest actions are exactly the ones it didn't press.
- No raw data: The crawl corpus is author-held 1. H1 and H4 need the authors or the sites.
- Effects are hard to see whole: A database snapshot misses files, queues, emails and calls to other services unless the test records them too.
- What is ours: The two-arm test, the three-stage cache outcomes, the pseudocode, and H1 to H4 are ours, not the paper's.
| Claim | Footnote(s) | Source type | Status |
|---|---|---|---|
| 407 of 10,000 sites (4.1%) had interchangeable requests: 12.7% of sites with a captured POST, 14.9% with a replayable one | [1] | Primary (preprint v1) | Established (one crawl) |
| 646 of 8,291 routes (7.8%) and 1,228 of 14,244 requests (8.6%) were interchangeable | [1] | Primary (preprint v1) | Established (one crawl) |
| 1,782 more requests matched but so did the no-parameter control, and were set aside as ambiguous | [1] | Primary (preprint v1) | Established |
| The crawl was unauthenticated and skipped destructive, session- changing and upload controls | [1] | Primary (preprint v1) | Established |
| The 1,228: 30.9% search, 15.1% analytics, 9.9% API, 8.9% auth, 7.7% unknown | [1] | Primary (preprint v1) | Established |
| SameSite=Lax cookies ride on cross-site top-level GET navigations but not cross- site POSTs | [1] | Primary (preprint v1) | Established |
| 7 of 115 token-bearing requests, on 2 sites, refused a forged token on POST and accepted it on GET; 78 showed no enforcement either way; state change and cross-site triggering not verified | [1] | Primary (measured) | Established (method- scoped gap) |
| 56 requests (9 sites) gave payload-induced cross- session cache hits; 136 (50 sites) were cacheable without one; no disclosure shown | [1] | Primary (measured) | Established |
| Safe methods must not execute client-requested unsafe actions; incidental effects such as logging may occur | [2] | Primary (standard) | Established |
| Django skips CSRF token checks on safe methods by design | [3] | Primary (documentation) | Established |
| Flask routes default to GET (with HEAD, OPTIONS); Django keeps request.GET and request.POST apart | [7][11] | Primary (documentation) | Established |
| Spring's @RequestMapping matches all methods by default; PHP's $_REQUEST merges $_GET, $_POST and $_COOKIE | [9][10] | Primary (documentation) | Established |
| Vanilla PHP and some Spring MVC and ASP.NET Core setups are the most prone | [1] | Primary (framework audit) | Established (mechanism only) |
| The frameworks behind the 407 sites have not been measured against the wider list | [1] | Primary (absence in the paper) | Unknown |
| CRS 4.0 rule 942100 targets ARGS, query and body alike | [6] | Primary (source) | Established |
| The #3585 and #3587 bypasses are payload obfuscation on end-of-life CRS 3.3.2, not method conversion | [4][5] | Primary (issue reports) | Established |
| Cache deception needs no state change | [8] | Secondary (security reference) | Established |
| H1: few converted GETs act unsafely | [1] | Ours | Hypothesis |
| H2: WAF parity within 5 points | [6] | Ours | Hypothesis |
| H3: PHP and Spring over- represented | [1] | Ours | Hypothesis |
| H4: some cache hits leak private content | [1] | Ours | Hypothesis |
References
- 01Golinelli, M., Hannachi, S. E., and Crispo, B. "When POST Becomes GET: Investigating HTTP Method Interchangeability and Its Security Implications." arXiv preprint arXiv:2610.37474v1, October 27, 2026. https://arxiv.org/abs/2610.37474v1 (accessed 2026-11-01). Artifact documentation: https://github.com/Golim/method-interchange/blob/main/ARTIFACT.md.
- 02Fielding, R., Nottingham, M., and Reschke, J. "HTTP Semantics." RFC 9110, June 2022, §9.2.1. https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.1 (accessed 2026-10-30).
- 03Django Software Foundation. "Cross Site Request Forgery protection." Django 5.2 documentation, undated; version 5.2 released April 2, 2025. https://docs.djangoproject.com/en/5.2/ref/csrf/ (accessed 2026-10-30).
- 04WAFSignal. "ModSecurity GET: SQLi bypass via scalar-expression and operator rewrites." owasp- modsecurity/ModSecurity issue #3585, opened June 27, 2026. https://github.com/owasp-modsecurity/ModSecurity/issues/3585 (accessed 2026-10-30).
- 05WAFSignal. "ModSecurity POST: SQLi bypass via request-body predicate/operator rewrites." owasp- modsecurity/ModSecurity issue #3587, opened June 27, 2026. https://github.com/owasp-modsecurity/ModSecurity/issues/3587 (accessed 2026-10-30).
- 06OWASP Core Rule Set. "REQUEST-942-APPLICATION-ATTACK-SQLI.conf," tag v4.0.0, rule 942100, 2024. https://github.com/coreruleset/coreruleset/blob/v4.0.0/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf (accessed 2026-10-30).
- 07Pallets Projects. "Quickstart: HTTP Methods." Flask 3.1.x documentation, undated. https://flask.palletsprojects.com/en/stable/quickstart/#http-methods (accessed 2026-10-30).
- 08PortSwigger. "Web cache deception." Web Security Academy, undated. https://portswigger.net/web-security/web-cache-deception (accessed 2026-10-30).
- 09Spring. "Mapping Requests." Spring Framework reference documentation, Web on Servlet Stack, Annotated Controllers. https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-requestmapping.html (accessed 2026-11-01).
- 10The PHP Group. "$_REQUEST." PHP Manual, Predefined Variables. https://www.php.net/manual/en/reserved.variables.request.php (accessed 2026-11-01).
- 11Django Software Foundation. "Request and response objects." Django 5.2 documentation. https://docs.djangoproject.com/en/5.2/ref/request-response/ (accessed 2026-11-01).