October 26, 2026
Critical Account Takeover via CSRF Header Bypass
بسم الله الرحمن الرحيم — وَمَا بِكُم مِّن نِّعْمَةٍ فَمِنَ اللَّهِ
By Ahmed Mamdouh
5 min read
collab with Alharbe0
Introduction
Sometimes, security bugs announce themselves with a dramatic stack trace or a server returning a suspicious 500 Internal Server Error.
And sometimes, the application simply accepts a request it should have rejected.
No fireworks. No dramatic music. Just an ordinary HTTP request doing something it wasn't supposed to do. 😅
During a bug bounty assessment of Canva Create's attendee portal, I discovered a Cross-Site Request Forgery (CSRF) vulnerability affecting the profile-update functionality.
The interesting part wasn't just the missing CSRF protection. The endpoint also behaved differently when the request's Content-Type was changed, opening the door to a crafted browser form submission.
Here's how I investigated the issue, what made it interesting, and why account-management workflows deserve extra scrutiny.
The Discovery 🔎
While exploring the attendee networking portal, I focused on the profile-editing functionality.
Profile updates might not sound particularly exciting from a security perspective. After all, changing a first name or job title doesn't exactly scream critical vulnerability.
But profile forms can contain much more sensitive fields, including the email address associated with an account.
That raised a simple question:
Would the server still accept a profile update if the request didn't include its expected CSRF protection?
Time to open Burp Suite.
Investigating the Request
After editing a test account's profile, I intercepted the corresponding request.
The original request used JSON:
POST /flow/common/networking/saveNetworkingProfileInformation?workflowApiToken=xxxx HTTP/2
Host: xxxx
Content-Type: application/json
Rfcsrf: [CSRF token]POST /flow/common/networking/saveNetworkingProfileInformation?workflowApiToken=xxxx HTTP/2
Host: xxxx
Content-Type: application/json
Rfcsrf: [CSRF token]The request body contained the profile fields and additional parameters expected by the application.
At this point, I wanted to determine whether the server actually enforced CSRF protection or merely expected a particular request structure.
Because, as every security tester eventually learns, a header being present in a request doesn't necessarily mean the server is enforcing it correctly.
The Unexpected Behavior
I modified the request in Burp Suite:
- Removed the
Rfcsrfheader. - Changed the content type to
application/x-www-form-urlencoded. - Adapted the request body to the new format.
- Submitted the modified request using a test account.
The endpoint accepted the request and processed the profile update without the expected CSRF token under the tested conditions.
That was the key observation.
The application appeared to handle the modified request differently from the original JSON request, allowing a state-changing operation to proceed without the expected protection.
One small request-format change, one missing security check, and suddenly the endpoint had some explaining to do. 👀
Building a Browser-Based Proof of Concept
The next step was to determine whether the behavior could be reproduced through an HTML form.
This matters because a CSRF vulnerability is not simply about sending a modified request from Burp Suite. The practical question is whether an attacker can cause a victim's authenticated browser to submit a request that the application accepts.
I created a proof of concept using a form submission targeting the affected endpoint.
The test involved the following components:
- The profile-update endpoint.
- The expected workflow parameters.
- Profile data encoded in the format accepted by the endpoint.
- A browser form capable of submitting the request.
The target URL and sensitive identifiers are redacted here:
<form method="POST" action="https://xxxx/flow/common/networking/saveNetworkingProfileInformation?workflowApiToken=xxxx">
<input type="hidden" name="workflowApiToken" value="xxxx">
<input type="hidden" name="valueMapJson" value="REDACTED_TEST_PROFILE_DATA">
<input type="hidden" name="objectId" value="REDACTED">
<input type="hidden" name="attendeeSettings" value="REDACTED">
<input type="submit" value="Submit">
</form><form method="POST" action="https://xxxx/flow/common/networking/saveNetworkingProfileInformation?workflowApiToken=xxxx">
<input type="hidden" name="workflowApiToken" value="xxxx">
<input type="hidden" name="valueMapJson" value="REDACTED_TEST_PROFILE_DATA">
<input type="hidden" name="objectId" value="REDACTED">
<input type="hidden" name="attendeeSettings" value="REDACTED">
<input type="submit" value="Submit">
</form>The form illustrates the general testing approach. Sensitive values and deployment-specific identifiers have intentionally been removed.
The important finding was that the server processed the profile-update operation despite the missing CSRF token in the tested request format.
Why This Was More Than a Profile-Editing Bug
A CSRF vulnerability becomes more serious when the affected action changes security-relevant account information.
In this case, the profile-update functionality included an email field, making the interaction between profile changes and account recovery particularly important.
The potential attack chain involved two separate behaviors:
- An attacker induces an authenticated user to submit a crafted profile-update request.
- The request changes the email address associated with the account.
- The attacker attempts to use the password-reset workflow to gain control of the account.
If the email change and recovery workflow can both be abused in this way, the combined issue may lead to Account Takeover (ATO).
There is an important distinction here: a successful CSRF-based profile update does not automatically prove account takeover. The complete chain depends on how the application validates email changes, verifies ownership of new email addresses, and handles password recovery.
In security research, the difference between possible impact and demonstrated impact matters.
A lot.
Potential Security Impact
If the complete attack chain is exploitable, the consequences could include:
Account integrity: An attacker could modify profile information without the account owner's intention.
Account ownership: If an unverified email change can be combined with a vulnerable password-reset workflow, an attacker may be able to take over an account.
Confidentiality: A successfully compromised account could expose information accessible to that account.
Trust and reputation: Account impersonation and unauthorized changes could affect users' confidence in the platform.
The actual severity depends on the application's authentication state requirements, browser behavior, email verification controls, and password-reset implementation.
Root Cause: Trusting the Request Too Much
The main issue was insufficient CSRF enforcement on a state-changing endpoint, combined with content-type handling that allowed the request to be processed without the expected CSRF token.
A robust implementation should not rely solely on a specific header being supplied by the client. The server must consistently enforce its CSRF defenses for every accepted request format.
The broader lesson is simple:
If an endpoint changes sensitive account data, every accepted request format must be protected — not just the one the frontend normally sends.
Recommended Fixes 🛠️
A few defensive measures can significantly reduce this class of risk:
- Enforce CSRF validation server-side. Reject state-changing requests that lack a valid CSRF token.
- Apply protections consistently. Do not bypass CSRF validation because a request uses a different content type.
- Restrict accepted content types. Ensure endpoints accept only the formats they are designed to process.
- Verify email changes. Require proof of ownership before treating a new email address as the account's verified address.
- Secure account recovery. Password-reset flows should not allow an unverified email change to transfer account ownership.
- Add defense in depth. Consider re-authentication, security notifications, and additional verification for sensitive account changes.
The goal isn't to make requests difficult to send. It's to make unauthorized requests fail — even when they look perfectly ordinary.
This assessment reinforced a few useful lessons:
- Test alternate content types. Applications may handle JSON and form-encoded requests differently.
- Don't confuse frontend behavior with server-side enforcement. The browser's normal request format is not a security boundary.
- Follow the data. A seemingly ordinary profile field may connect to more sensitive account workflows.
- Validate the entire attack chain. Demonstrate each step before claiming a full account takeover.
- Keep your evidence clean. A reproducible request and a controlled test account are more useful than dramatic claims unsupported by the PoC.
And perhaps the most important lesson:
Sometimes the bug isn't hiding in a complicated exploit. It's hiding in the assumption that the server will always receive requests the way the developers intended.
Spoiler alert: attackers don't have to follow the frontend's rules. 😉
Responsible Disclosure
I reported the issue through Canva's bug bounty program. The report was accepted, and I received a bounty.
created the submission (29 Jun 2025 12:04:45)
respond to comments (02 Jul 2025 16:31:10)
changed the state to Triaged (09 Jul 2025 16:38:13 )
changed the state to Resolved(15 Aug 2025 00:23:41)
rewarded duckeg97 $$$$ (15 Aug 2025 00:30:25)
thanks