Broken Access Control: The Complete Practical Guide
Parameter-Based Access Control Flaws
When apps trust a client-supplied role or flag: hidden form fields, query params, cookies, headers, and editable token claims, and why that is broken.
Published
A large share of access control bugs come down to one mistake: the app lets the client carry the authorization decision. A role in a hidden field, a flag in a query string, a permission in a cookie, a claim in a token, anywhere the server reads "who you are allowed to be" from your request, you simply tell it you are an administrator.
When the app trusts a client-supplied role
The pattern to hunt for is any privilege value that travels in the request. If the server acts on it instead of deriving your role from its own session state, it is trusting you to be honest about your own permissions. You are not obliged to be.
The test is always the same: find the value, raise it, resend.
Hidden form fields and query params
The UI often sends more than it shows. A form may include a hidden role field, or an endpoint may accept a flag in the query string that the interface never sets:
?admin=true ?role=admin ?is_admin=1 ?debug=1
&roleid=1 &access_level=9 &group=administratorsAdd these even when the UI never sends them. Apps frequently check for a parameter they assume only their own front end would include.
Cookies and headers that carry the role
Authorization state is sometimes stored where it looks tamper-proof but is not:
Cookie: role=user; isAdmin=false -> role=admin; isAdmin=true
X-Role: user -> X-Role: adminA cookie or header is just client input. If the server reads your role from it, change it.
Editable token claims
Tokens carry claims like role or admin, and the whole point of signing them is so the client cannot edit them. That protection only holds if the server verifies the signature. Apps that skip verification, accept an unsigned token, or trust a claim without re-checking it against server data are the same parameter-trust bug wearing a token. Decode the token, look for a privilege claim, and test whether the server actually validates it before trusting it.
Why any client-held authorization decision is broken
The principle underneath every example here: the client controls everything it sends, so the server can never use client input as the source of truth for permissions. A role is not something you submit, it is something the server looks up for your authenticated identity and enforces. The moment that decision lives in a field, a cookie, a header, or an unverified claim, it is yours to rewrite.
The fix is to derive authorization server-side from the session and check it against server-held data on every request, never reading a privilege value from the request body or headers. When a gate relies on the request shape rather than a value, you are looking at platform misconfiguration instead. Try flipping a role on a free live target.
← Back to Broken Access Control: The Complete Practical Guide