<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: shemkar</title>
    <description>The latest articles on DEV Community by shemkar (@shemkar).</description>
    <link>https://dev.to/shemkar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3794510%2Fc473b802-88d2-42eb-b4ab-c701179fa2cf.png</url>
      <title>DEV Community: shemkar</title>
      <link>https://dev.to/shemkar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shemkar"/>
    <language>en</language>
    <item>
      <title>One Extra JSON Key: How a Harmless Profile Endpoint Became an ATO Candidate</title>
      <dc:creator>shemkar</dc:creator>
      <pubDate>Sun, 26 Apr 2026 21:04:52 +0000</pubDate>
      <link>https://dev.to/shemkar/one-extra-json-key-how-a-harmless-profile-endpoint-became-an-ato-candidate-57ni</link>
      <guid>https://dev.to/shemkar/one-extra-json-key-how-a-harmless-profile-endpoint-became-an-ato-candidate-57ni</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcx1jnarwp8hv2m98caqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcx1jnarwp8hv2m98caqr.png" alt=" " width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The harmless profile endpoint that taught me how real bugs work&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Early in my bug bounty journey, I found a bug that looked simple from the outside, but it changed the way I think about web security.&lt;/p&gt;

&lt;p&gt;At that time, I didn’t know how to write strong reports.&lt;br&gt;
I knew how to test manually.&lt;br&gt;
I knew how to follow weird backend behavior.&lt;br&gt;
But I didn’t fully understand that finding a bug is only half of the work.&lt;/p&gt;

&lt;p&gt;The other half is proving impact clearly.&lt;/p&gt;

&lt;p&gt;The bug started from a normal profile update endpoint.&lt;/p&gt;

&lt;p&gt;Nothing fancy.&lt;br&gt;
No admin panel.&lt;br&gt;
No scanner.&lt;br&gt;
No magic.&lt;/p&gt;

&lt;p&gt;Just this kind of request:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test target&lt;/strong&gt; &lt;code&gt;https://example.com&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;PATCH&lt;/span&gt; &lt;span class="nn"&gt;/api/users/me&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;session=&amp;lt;redacted&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time_zone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Africa/Casablanca"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, this endpoint looked harmless.&lt;/p&gt;

&lt;p&gt;It was supposed to update normal user-controlled profile fields like timezone, language, or basic settings.&lt;/p&gt;

&lt;p&gt;But I asked myself one simple question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if the backend receives fields that the frontend never sends?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I added sensitive parameters manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;PATCH&lt;/span&gt; &lt;span class="nn"&gt;/api/users/me&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;session=&amp;lt;redacted&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time_zone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Africa/Casablanca"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"attacker@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_instructor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A secure backend should never allow a normal user to control fields like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"attacker@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_instructor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend is not security.&lt;/p&gt;

&lt;p&gt;Even if the UI never sends &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;is_instructor&lt;/code&gt;, or sensitive account fields, an attacker can still add them manually in Burp Suite, curl, or any HTTP client.&lt;/p&gt;

&lt;p&gt;The real security decision must happen on the backend.&lt;/p&gt;

&lt;p&gt;In my case, the server accepted the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back then, I was excited because I saw &lt;code&gt;200 OK&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But later I learned one of the most important lessons in bug bounty:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A 200 OK is not impact.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Persistence is impact.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;A protected action unlocked by that persistence is impact.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So the real questions are not:&lt;/p&gt;

&lt;p&gt;“Did the server return 200?”&lt;/p&gt;

&lt;p&gt;The real questions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the email actually change?&lt;/li&gt;
&lt;li&gt;Was password confirmation bypassed?&lt;/li&gt;
&lt;li&gt;Was email verification bypassed?&lt;/li&gt;
&lt;li&gt;Did the role change only in the UI, or did it affect authorization?&lt;/li&gt;
&lt;li&gt;Could the attacker use password reset after changing the email?&lt;/li&gt;
&lt;li&gt;Could the user access instructor/admin-only functionality after modifying role-related fields?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is where a harmless profile endpoint can become dangerous.&lt;/p&gt;

&lt;p&gt;This pattern is called &lt;strong&gt;Mass Assignment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It happens when the backend maps incoming JSON fields directly into a user object or database model without a strict allowlist.&lt;/p&gt;

&lt;p&gt;The developer expects something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time_zone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Africa/Casablanca"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the backend also accepts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time_zone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Africa/Casablanca"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"attacker@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_instructor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is not that the attacker can send extra fields.&lt;/p&gt;

&lt;p&gt;Anyone can send extra fields.&lt;/p&gt;

&lt;p&gt;The problem is when the backend trusts them.&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;Account Takeover&lt;/strong&gt; angle, the proof should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Attacker has temporary access to an authenticated session.
2. Attacker changes the account email through the API.
3. No password confirmation is required.
4. No old email verification is required.
5. Attacker logs out.
6. Attacker uses "Forgot Password".
7. Password reset link goes to the attacker-controlled email.
8. Victim loses control of the account.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the &lt;strong&gt;Privilege Escalation&lt;/strong&gt; angle, the proof must be stronger than just saying &lt;code&gt;"role":"admin"&lt;/code&gt; was accepted.&lt;/p&gt;

&lt;p&gt;The important proof is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/api/instructor/dashboard&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;session=&amp;lt;regular_user_session&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before the attack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;403&lt;/span&gt; &lt;span class="ne"&gt;Forbidden&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;PATCH&lt;/span&gt; &lt;span class="nn"&gt;/api/users/me&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;session=&amp;lt;regular_user_session&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_instructor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the attack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/api/instructor/dashboard&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
&lt;span class="na"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;session=&amp;lt;same_regular_user_session&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the result becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is real impact.&lt;/p&gt;

&lt;p&gt;Not the field.&lt;br&gt;
Not the response.&lt;br&gt;
The action unlocked by the field.&lt;/p&gt;

&lt;p&gt;That was the mistake I made in the beginning.&lt;/p&gt;

&lt;p&gt;I focused too much on the parameter modification, and not enough on the final proof chain.&lt;/p&gt;

&lt;p&gt;I thought:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“The role changed. The email changed. This is obviously serious.”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But triagers and developers do not work with emotion.&lt;/p&gt;

&lt;p&gt;They need a clean chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before → blocked
Exploit → accepted
After → protected action unlocked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bug pushed me to improve.&lt;/p&gt;

&lt;p&gt;It taught me that business logic bugs are not about sending random payloads.&lt;/p&gt;

&lt;p&gt;They are about understanding trust.&lt;/p&gt;

&lt;p&gt;Who controls this field?&lt;br&gt;
Who should control this field?&lt;br&gt;
What changes if this value is accepted?&lt;br&gt;
What can I do after the backend persists it?&lt;/p&gt;

&lt;p&gt;That mindset changed how I hunt.&lt;/p&gt;

&lt;p&gt;Now, when I see an endpoint like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;PATCH /api/users/me
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don’t only test the fields visible in the UI.&lt;/p&gt;

&lt;p&gt;I test the trust boundary.&lt;/p&gt;

&lt;p&gt;I ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the backend use an allowlist?&lt;/li&gt;
&lt;li&gt;Does it ignore unexpected fields?&lt;/li&gt;
&lt;li&gt;Can I modify email without re-authentication?&lt;/li&gt;
&lt;li&gt;Can I modify role-like fields?&lt;/li&gt;
&lt;li&gt;Can I modify verification flags?&lt;/li&gt;
&lt;li&gt;Can I modify account type, plan, permissions, or security settings?&lt;/li&gt;
&lt;li&gt;Does the value persist server-side?&lt;/li&gt;
&lt;li&gt;Does it unlock a real action?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers, the fix is not “hide the field from the frontend”.&lt;/p&gt;

&lt;p&gt;The fix must be server-side.&lt;/p&gt;

&lt;p&gt;A secure backend should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a strict allowlist of editable fields.&lt;/li&gt;
&lt;li&gt;Reject or ignore sensitive fields like &lt;code&gt;role&lt;/code&gt;, &lt;code&gt;is_admin&lt;/code&gt;, &lt;code&gt;is_instructor&lt;/code&gt;, &lt;code&gt;email_verified&lt;/code&gt;, &lt;code&gt;permissions&lt;/code&gt;, &lt;code&gt;plan&lt;/code&gt;, &lt;code&gt;balance&lt;/code&gt;, &lt;code&gt;account_type&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Require password confirmation or re-authentication for sensitive account changes.&lt;/li&gt;
&lt;li&gt;Require email verification before replacing the primary email.&lt;/li&gt;
&lt;li&gt;Never trust frontend-controlled authorization fields.&lt;/li&gt;
&lt;li&gt;Log sensitive account modifications.&lt;/li&gt;
&lt;li&gt;Add rate limits and security challenges for repeated email changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A secure response should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;PATCH&lt;/span&gt; &lt;span class="nn"&gt;/api/users/me&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"time_zone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Africa/Casablanca"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected secure behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt; &lt;span class="ne"&gt;Bad Request&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"role is not an editable field"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bug did not pay me.&lt;/p&gt;

&lt;p&gt;But it paid me in experience.&lt;/p&gt;

&lt;p&gt;It taught me that a boring endpoint can hide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mass Assignment&lt;/li&gt;
&lt;li&gt;Missing re-authentication&lt;/li&gt;
&lt;li&gt;Email change bypass&lt;/li&gt;
&lt;li&gt;Account takeover chains&lt;/li&gt;
&lt;li&gt;Weak authorization logic&lt;/li&gt;
&lt;li&gt;Privilege escalation&lt;/li&gt;
&lt;li&gt;Dangerous backend trust assumptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also taught me that a weak report can kill a strong bug.&lt;/p&gt;

&lt;p&gt;Today, I try to prove impact better.&lt;/p&gt;

&lt;p&gt;Not just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What changed?
Where did it persist?
What security control was bypassed?
What protected action became possible?
How can the developer reproduce it in 2 minutes?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the difference between guessing and understanding.&lt;/p&gt;

&lt;p&gt;No target names.&lt;br&gt;
No private data.&lt;br&gt;
No drama.&lt;/p&gt;

&lt;p&gt;Just one lesson:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sometimes the most dangerous bugs are not hidden in complex exploits.&lt;br&gt;
Sometimes they are hidden in one extra JSON key.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webtesting</category>
      <category>api</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
