Safeguard
Application Security•

CWE-650 Trusting HTTP Methods for Access Control

Assuming GET never changes state lets attackers modify or delete data through endpoints that only block POST, PUT and DELETE.

Safeguard Security Team
2 min read

CWE-650 is a protection mechanism built on the belief that a GET request is harmless. The HTTP specification says GET should not have side effects, but nothing in the protocol enforces it. If your code accepts GET and changes data, a rule that only restricts POST, PUT and DELETE does not protect anything. REST-style web services are the usual place for this.

How it happens

A team locks down write methods on a resource and assumes the job is done. Elsewhere, a developer wrote a handler that deletes a record when it receives a GET, because it was convenient or because a link had to work from an email. MITRE notes it is common practice with REST web services to have GET requests modify resources on the server side.

# Vulnerable: a state change on GET, guarded only by a method rule upstream
@app.route("/items/<id>/delete")
def delete_item(id):
    db.delete(id)
    return "ok"

What an attacker gains

MITRE lists three consequences: escalating privileges, modifying application data (resource modification and deletion), and reading sensitive application data.

How to find it

The record does not list detection methods. Review the route table: list every handler reachable by GET, and check whether any of them writes, deletes or triggers an action. Then try each protected resource with every method, GET included, as an unprivileged user.

How to fix it

MITRE's mitigation is to configure ACLs on the server side so the right level of access control is defined for each accessible resource representation. Practically:

  1. Enforce authorization in the handler, regardless of method.
  2. Never perform state changes on GET. Use POST, PUT, PATCH or DELETE and check permissions on those.
  3. Deny by default, and allow methods per route explicitly.
@app.route("/items/<id>", methods=["DELETE"])
@require_permission("items:delete")
def delete_item(id):
    db.delete(id)
    return "", 204

Quick check

Does the HTTP spec stop a server from changing data on GET?

No. It says GET should be free of side effects, but a server can still implement a GET that creates, updates or deletes data.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.