Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import csharp
import semmle.code.csharp.frameworks.system.Web
import semmle.code.csharp.frameworks.system.web.Helpers
import semmle.code.csharp.frameworks.system.web.Mvc
import semmle.code.csharp.frameworks.microsoft.AspNetCore as AspNetCore

private Method getAValidatingMethod() {
result = any(AntiForgeryClass a).getValidateMethod()
Expand All @@ -35,6 +36,8 @@ private Method getAStartedMethod() {

/**
* Holds if the project has a global anti forgery filter.
*
* No AspNetCore case here as the corresponding class doesn't seem to exist.
*/
predicate hasGlobalAntiForgeryFilter() {
// A global filter added
Expand All @@ -48,16 +51,30 @@ predicate hasGlobalAntiForgeryFilter() {
)
}

from Controller c, Method postMethod
predicate isUnvalidatedPostMethod(Class c, Method m) {
c.(Controller).getAPostActionMethod() = m and
not m.getAnAttribute() instanceof ValidateAntiForgeryTokenAttribute and
not c.getAnAttribute() instanceof ValidateAntiForgeryTokenAttribute
or
c.(AspNetCore::MicrosoftAspNetCoreMvcController).getAnActionMethod() = m and
m.getAnAttribute() instanceof AspNetCore::MicrosoftAspNetCoreMvcHttpPostAttribute and
not m.getAnAttribute() instanceof AspNetCore::ValidateAntiForgeryAttribute and
not c.getAnAttribute() instanceof AspNetCore::ValidateAntiForgeryAttribute
}

Element getAValidatedElement() {
any(ValidateAntiForgeryTokenAttribute a).getTarget() = result
or
any(AspNetCore::ValidateAntiForgeryAttribute a).getTarget() = result
}

from Class c, Method postMethod
where
postMethod = c.getAPostActionMethod() and
// The method is not protected by a validate anti forgery token attribute
not postMethod.getAnAttribute() instanceof ValidateAntiForgeryTokenAttribute and
not c.getAnAttribute() instanceof ValidateAntiForgeryTokenAttribute and
isUnvalidatedPostMethod(c, 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(ValidateAntiForgeryTokenAttribute a, Element e | e = a.getTarget()) and
exists(getAValidatedElement()) and
// Also ignore cases where a global anti forgery filter is in use.
not hasGlobalAntiForgeryFilter()
select postMethod,
Expand Down
4 changes: 4 additions & 0 deletions csharp/ql/src/change-notes/2025-12-08-csrf-aspnetcore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `Missing cross-site request forgery token validation` query was extended to support ASP.NET Core.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Mvc;

public class HomeController : Controller
{
// BAD: Anti forgery token has been forgotten
[HttpPost]
public ActionResult Login()
{
return View();
}

// GOOD: Anti forgery token is validated
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateDetails()
{
return View();
}

// No validation required, as this is a GET method.
public ActionResult ShowHelp()
{
return View();
}

// Should be ignored, because it is not an action method
[NonAction]
public void UtilityMethod()
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| MissingAntiForgeryTokenValidation.cs:7:25:7:29 | Login | Method 'Login' handles a POST request without performing CSRF token validation. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
query: Security Features/CWE-352/MissingAntiForgeryTokenValidation.ql

Check warning

Code scanning / CodeQL

Query test without inline test expectations Warning test

Query test does not use inline test expectations.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
semmle-extractor-options: /nostdlib /noconfig
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../../resources/stubs/_frameworks/Microsoft.AspNetCore.App/Microsoft.AspNetCore.App.csproj