API-81Required
Accept GraphQL mutations only by POST with a JSON body
Run GraphQL mutations only from POST requests with Content-Type: application/json. Refuse a mutation sent by GET with 405 and an Allow header, and refuse POST bodies of any other content type.
How to do it
A browser sends a GET, or a POST with a form or plain text body, to any site without asking. When such a request can run a mutation with the visitor's cookie, any web page can act as them. A JSON content type forces the browser to ask first through a CORS preflight. The GraphQL over HTTP draft says GET must not run mutations, and it names application/graphql-response+json for answers. GET stays useful for cached queries. Lock the endpoint down as lock down GraphQL endpoints says, and block cross-site posts as block cross-site form posts does.
Examples
JavaScript example
app.use('/graphql', (req, res, next) => {
if (req.method === 'POST' && !req.is('application/json')) return res.status(415).end();
if (req.method === 'GET' && /^\s*mutation\b/.test(String(req.query.query ?? ''))) {
return res.status(405).set('Allow', 'POST').end();
}
next();
});
Python example
from starlette.responses import Response
async def graphql_guard(request, call_next):
if request.url.path == "/graphql":
if request.method == "POST" and request.headers.get("content-type", "").split(";")[0] != "application/json":
return Response(status_code=415)
if request.method == "GET" and request.query_params.get("query", "").lstrip().startswith("mutation"):
return Response(status_code=405, headers={"Allow": "POST"})
return await call_next(request)
How to check
Send a mutation by GET. The answer is 405 with Allow: POST and nothing changes. Send the same mutation by POST with Content-Type: text/plain and with a form body. Both are refused, and nothing changes.
Sources
Related rules
- SEC-102 Lock down GraphQL endpoints in Adversarial testing
- SEC-23 Block cross-site form posts in Adversarial testing
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/api-design/graphql-and-grpc/graphql-post-mutations">Agent Wranglers API design playbook</a>. In code you write, add a comment next to it with the same link.