Skip to content

[C#] Recognize RequireAntiforgeryToken attributes - #22322

Open
theinfosecguy wants to merge 2 commits into
github:mainfrom
theinfosecguy:csharp-require-antiforgery-token
Open

[C#] Recognize RequireAntiforgeryToken attributes#22322
theinfosecguy wants to merge 2 commits into
github:mainfrom
theinfosecguy:csharp-require-antiforgery-token

Conversation

@theinfosecguy

Copy link
Copy Markdown
Contributor

Recognize enabled RequireAntiforgeryToken attributes when UseAntiforgery() is configured.

This handles inherited attributes, explicit opt-outs, and existing MVC antiforgery filters.

Fixes #22238

@theinfosecguy
theinfosecguy requested a review from a team as a code owner August 11, 2026 19:03
@michaelnebel
michaelnebel self-requested a review August 12, 2026 14:56

@michaelnebel michaelnebel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for the contribution! We really appreciate all the help we can get on keeping the queries up to date!

I have added a suggestion for a minor re-write of the query logic.

}

bindingset[controller, method]
private predicate hasAspNetCoreAntiForgeryValidation(Class controller, Method method) {

@michaelnebel michaelnebel Aug 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, we can refactor/encapsulate some of the logic and avoid using the bindingset pragmas.

Maybe the remaining part of the query can look something like:

class MvcControllerPostMethod extends Method {
  private Controller controller;

  MvcControllerPostMethod() { controller.(Controller).getAPostActionMethod() = this }

  predicate hasValidateAntiForgeryAttribute() {
    this.getAnAttribute() instanceof ValidateAntiForgeryTokenAttribute or
    controller.getABaseType*().getAnAttribute() instanceof ValidateAntiForgeryTokenAttribute
  }
}

class AspNetCoreControllerPostMethod extends Method {
  private AspNetCore::MicrosoftAspNetCoreMvcController controller;

  AspNetCoreControllerPostMethod() {
    controller.(AspNetCore::MicrosoftAspNetCoreMvcController).getAnActionMethod() = this and
    this.getAnAttribute() instanceof AspNetCore::MicrosoftAspNetCoreMvcHttpPostAttribute
  }

  predicate hasValidateAntiForgeryAttribute() {
    this.getAnAttribute() instanceof AspNetCore::ValidateAntiForgeryAttribute or
    controller.getABaseType*().getAnAttribute() instanceof AspNetCore::ValidateAntiForgeryAttribute
  }

  predicate hasRequireAntiForgeryAttribute() {
    hasAspNetCoreAntiForgeryMiddleware() and
    (
      getEffectiveRequireAntiforgeryTokenAttributeOnMethod(this).requiresValidation()
      or
      not exists(getEffectiveRequireAntiforgeryTokenAttributeOnMethod(this)) and
      getEffectiveRequireAntiforgeryTokenAttributeOnClass(controller).requiresValidation()
    )
  }
}

predicate isUnvalidatedAspNetCorePostMethod(AspNetCoreControllerPostMethod m) {
  not m.hasValidateAntiForgeryAttribute() and
  not m.hasRequireAntiForgeryAttribute()
}

predicate isUnvalidatedMvcPostMethod(MvcControllerPostMethod m) {
  not m.hasValidateAntiForgeryAttribute()
}

predicate isUnvalidatedPostMethod(Method m) {
  isUnvalidatedMvcPostMethod(m) or
  isUnvalidatedAspNetCorePostMethod(m)
}

Element getAValidatedElement() {
  any(ValidateAntiForgeryTokenAttribute a).getTarget() = result
  or
  any(AspNetCore::ValidateAntiForgeryAttribute a).getTarget() = result
  or
  hasAspNetCoreAntiForgeryMiddleware() and
  any(RequireAntiforgeryTokenAttribute a | a.requiresValidation()).getTarget() = result
}

from Method postMethod
where
  isUnvalidatedPostMethod(postMethod) and
  // Verify that validate anti forgery token attributes are used somewhere within this project, to
  // avoid reporting false positives on projects that use an alternative approach to mitigate CSRF
  // issues.
  exists(getAValidatedElement()) and
  // Also ignore cases where a global anti forgery filter is in use.
  not hasGlobalAntiForgeryFilter()
select postMethod,
  "Method '" + postMethod.getName() +
    "' handles a POST request without performing CSRF token validation."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that then it is also possible to remove the bindingset pragmas on the get* like predicates.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. I’ve applied the refactor and removed the bindingset pragmas.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the C# CSRF query to recognize RequireAntiforgeryToken when ASP.NET Core antiforgery middleware is configured.

Changes:

  • Models enabled, disabled, inherited, and overridden antiforgery metadata.
  • Preserves MVC filter validation behavior.
  • Adds middleware-present and middleware-absent regression tests.
Show a summary per file
File Description
require-antiforgery-without-middleware/options Configures framework stubs.
require-antiforgery-without-middleware/MissingAntiForgeryTokenValidation.qlref Selects the tested query.
require-antiforgery-without-middleware/MissingAntiForgeryTokenValidation.expected Records the expected alert.
require-antiforgery-without-middleware/MissingAntiForgeryTokenValidation.cs Tests metadata without middleware.
missing-aspnetcore/MissingAntiForgeryTokenValidation.expected Updates expected ASP.NET Core results.
missing-aspnetcore/MissingAntiForgeryTokenValidation.cs Tests metadata variants and inheritance.
disabled-require-antiforgery/options Configures framework stubs.
disabled-require-antiforgery/MissingAntiForgeryTokenValidation.qlref Selects the tested query.
disabled-require-antiforgery/MissingAntiForgeryTokenValidation.expected Records no expected results.
disabled-require-antiforgery/MissingAntiForgeryTokenValidation.cs Tests disabled-only metadata behavior.
MissingAntiForgeryTokenValidation.ql Adds antiforgery metadata and middleware recognition.
2026-08-09-require-antiforgery-token.md Documents the analysis improvement.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 11/12 changed files
  • Comments generated: 0
  • Review effort level: Balanced

@michaelnebel michaelnebel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good! Thank you very much for improving the query!
I have started a DCA (test) run - if it doesn't show any problems then we can merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive: "Missing cross-site request forgery token validation" does not recognize [RequireAntiforgeryToken]

3 participants