What Is CSRF Detecting CSRF Vulnerabilities in Pull Requests

What Is CSRF Detecting CSRF Vulnerabilities in Pull Requests

8 min read
GitHubGitHub Pull Request SecurityCSRF
Featured image for What Is CSRF Detecting CSRF Vulnerabilities in Pull Requests

What Is CSRF? Detecting CSRF Vulnerabilities in Pull Requests

Cross-Site Request Forgery (CSRF) remains one of the most persistent web application vulnerabilities, even though it has been documented in the OWASP Top Ten for over a decade. For engineering teams that ship code through pull requests, CSRF is particularly dangerous because it doesn't always look like a bug. A missing token, a misconfigured cookie flag, or a state-changing endpoint that accepts GET requests can slip past reviewers who are focused on logic and functionality rather than authentication boundaries. This article breaks down what CSRF actually is, how it works, and — more practically — what to look for when reviewing pull requests so these issues get caught before they reach production.

What Is CSRF?

CSRF is an attack that tricks an authenticated user's browser into submitting a request to a web application without the user's knowledge or consent. The attacker doesn't need to steal a session token or bypass authentication. Instead, they exploit the fact that browsers automatically attach cookies (including session cookies) to requests sent to a domain, regardless of which site initiated the request.

Here's the basic mechanism:

  1. A user logs into a legitimate site, such as a banking application, and receives a session cookie.
  2. Without logging out, the user visits a malicious or compromised website.
  3. That malicious page contains a hidden form, an image tag, or a script that automatically submits a request to the banking application — for example, a fund transfer.
  4. Because the user's browser still holds a valid session cookie for the banking site, the request is sent with that cookie attached.
  5. The banking application processes the request as if the legitimate user initiated it, because from the server's perspective, the request looks authentic.

The attacker never sees the user's credentials or session token. They simply weaponize the browser's trust model and the user's existing authenticated session. CSRF typically targets state-changing actions: changing an email address, updating account settings, transferring funds, deleting data, or modifying permissions. Read-only endpoints are less attractive targets because there's no state to manipulate, though data exposure through CSRF-triggered actions is also possible in some designs.

Why CSRF Still Happens

CSRF exists because HTTP requests, by default, carry no built-in mechanism to prove that the request truly originated from the application's own pages. Browsers send cookies automatically based on domain matching, not based on which page or origin triggered the request. Unless a developer explicitly adds protection, any form or fetch request from any origin can trigger a valid request to your server, as long as the user's browser holds a valid session cookie.

Frameworks have gotten much better at mitigating this by default — Django, Rails, and ASP.NET all ship with CSRF protection enabled out of the box for form-based submissions. But protection gaps commonly reappear when teams introduce custom API endpoints, migrate to single-page applications, add new authentication flows, or build internal admin tools where "we'll add security later" becomes the default mindset.

How CSRF Protection Actually Works

Before reviewing PRs for CSRF issues, it helps to understand the standard defenses so you know what "correct" looks like in a diff.

Synchronizer Token Pattern: The server generates a unique, unpredictable token tied to the user's session and embeds it in forms or API responses. Every state-changing request must include this token, and the server validates it before processing. An attacker's forged request has no way to know or include this token.

Double Submit Cookie: The server sets a CSRF token as a cookie and also expects it in a request header or form field. The server compares the two values. Since a malicious site cannot read cookies belonging to another domain, it cannot replicate the value in the request body or header.

SameSite Cookie Attribute: Setting SameSite=Lax or SameSite=Strict on session cookies instructs the browser not to send that cookie on cross-site requests, which significantly reduces CSRF risk without requiring token logic. SameSite=Strict blocks the cookie on essentially all cross-site navigation; SameSite=Lax (the current default in most modern browsers) still allows the cookie on top-level GET navigations but blocks it on cross-site POST requests.

Custom Request Headers: Requiring a custom header (such as X-Requested-With) on state-changing API calls can help, since simple cross-site form submissions cannot set custom headers without triggering a CORS preflight check, which the server can then reject.

Re-authentication for Sensitive Actions: For highly sensitive operations (password changes, payment details), requiring the user to re-enter their password or confirm via a secondary factor adds a layer that CSRF alone cannot bypass.

Detecting CSRF Vulnerabilities in Pull Requests

Code review is one of the most effective and underused places to catch CSRF issues, because the vulnerability is often introduced at the moment a new endpoint or form is written — before it's ever tested with a security scanner. Here's what reviewers should specifically look for.

  1. New State-Changing Endpoints Without Token Validation

Any new route that performs a POST, PUT, PATCH, or DELETE operation should be checked for CSRF token validation. If the framework provides middleware or decorators for this (such as Django's @csrf_protect or Rails' protect_from_forgery), confirm the new endpoint isn't explicitly exempted unless there's a clear, documented reason. 2. Exemptions and Bypass Flags

Search the diff for anything that disables CSRF protection: @csrf_exempt, skip_authorization, CSRF_COOKIE_SECURE = False, or similar flags. These are sometimes added temporarily during debugging and forgotten in the final PR. Every exemption should have a comment explaining why it's safe (for example, the endpoint is protected by a different mechanism like an API key with strict CORS rules). 3. State-Changing Actions Triggered by GET Requests

GET requests are especially dangerous because they can be triggered by a simple <img> tag or link, with no JavaScript required. Reviewers should flag any GET endpoint that deletes, updates, or creates data. If moving the action to POST isn't feasible immediately, it should at minimum require a valid CSRF token and not rely solely on session cookies. 4. Cookie Configuration Changes

Any modification to cookie-setting code deserves scrutiny. Look for the SameSite attribute being removed, downgraded from Strict/Lax to None, or the Secure flag being dropped. SameSite=None requires Secure and effectively re-opens the cross-site request path that SameSite was meant to close. 5. New Frontend Fetch or Axios Calls

When reviewing frontend changes, check whether new API calls that mutate data include the CSRF token in headers (commonly X-CSRF-Token or similar) and whether credentials: 'include' or withCredentials: true is paired with proper origin validation on the server side. 6. CORS Configuration Alongside CSRF Logic

CSRF and CORS are different mechanisms but often reviewed together. A wildcard Access-Control-Allow-Origin: * combined with Access-Control-Allow-Credentials: true is invalid per spec and browsers will reject it, but overly permissive origin allowlists (regex patterns that are too broad, or reflecting the request origin without validation) can still undermine CSRF protections indirectly by widening which sites can interact with authenticated endpoints. 7. Third-Party Webhook or Callback Endpoints

New endpoints designed to receive webhooks (payment providers, OAuth callbacks, integration partners) are often — correctly — exempted from standard CSRF protection since they aren't called by a logged-in browser session. But reviewers should confirm these endpoints use their own verification, such as signature validation on the payload, rather than being left open with no protection at all.

Building CSRF Checks Into Your Review Process

Manual review catches issues automated tools miss, particularly around business logic and intent, but it shouldn't be the only line of defense. Static analysis tools (such as Semgrep or CodeQL) can be configured with rules that flag missing CSRF decorators, disabled protections, or risky cookie configurations automatically as part of CI, before a human reviewer even opens the diff. Pairing automated static analysis with a reviewer checklist — specifically asking "does this new endpoint change state, and if so, how is it protected against forged requests?" — closes most of the gap between functional correctness and security correctness.

Conclusion

CSRF is not a new or exotic vulnerability, but it continues to surface in real applications because protection has to be applied consistently across every state-changing endpoint, every cookie configuration, and every new frontend request — and consistency is exactly what slips during fast-moving development. Treating CSRF review as a standard part of the pull request process, backed by both a reviewer checklist and automated static analysis, is one of the most reliable ways to keep this class of vulnerability out of production.

For a complete guide on pull request security read our guide

Try Launchioo on your repositories

Install the GitHub App and get automated pull request security reviews in minutes.

Related articles