Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing cross-site request forgery token validation query (experimental) #16942

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

aegilops
Copy link
Contributor

@aegilops aegilops commented Jul 9, 2024

New version of the existing cs/web/missing-token-validation query that adds:

  1. support for AspNetCore
  2. lower tolerance for false negatives

Any POST method without either an explicit CSRF attribute, or an identifable global CSRF filter, will be reported as an alert.

This is in contrast to the existing approach, which only alerts if one method is missing a CSRF filter, where others are not.

The existing approach is quite permissive, and results in missing results in both synthetic web applications and in real applications.

@aegilops aegilops requested a review from a team as a code owner July 9, 2024 17:07
Copy link
Contributor

github-actions bot commented Jul 9, 2024

QHelp previews:

csharp/ql/src/experimental/CWE-352/MissingAntiForgeryTokenValidationAspNetCore.qhelp

Missing cross-site request forgery token validation

Web applications that use tokens to prevent cross-site request forgery (CSRF) should validate the tokens for all HTTP POST requests.

Although login and authentication methods are not vulnerable to traditional CSRF attacks, they still need to be protected with a token or other mitigation. This because an unprotected login page can be used by an attacker to force a login using an account controlled by the attacker. Subsequent requests to the site are then made using this account, without the user being aware that this is the case. This can result in the user associating private information with the attacker-controlled account.

Recommendation

The appropriate attribute should be added to this method to ensure the anti-forgery token is validated when this action method is called. If using the MVC-provided anti-forgery framework this will be the [ValidateAntiForgeryToken] attribute.

Alternatively, you may consider including a global filter that applies token validation to all POST requests.

Example

In the following example an ASP.NET MVC Controller is using the [ValidateAntiForgeryToken] attribute to mitigate against CSRF attacks. It has been applied correctly to the UpdateDetails method. However, this attribute has not been applied to the Login method. This should be fixed by adding this attribute.

using System.Web.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();
    }
}

References

private import semmle.code.csharp.frameworks.system.web.Mvc as Mvc
private import semmle.code.csharp.frameworks.microsoft.AspNetCore as AspNetCore

/** Defines classes used for anti-forgery tokens. */

Check warning

Code scanning / CodeQL

Class QLDoc style. Warning

The QLDoc for a class should start with 'A', 'An', or 'The'.
result = this.(AspNetCore::AntiForgeryClass).getValidateMethod()
}
}
/** Defines the anti-forgery token attribute. */

Check warning

Code scanning / CodeQL

Class QLDoc style. Warning

The QLDoc for a class should start with 'A', 'An', or 'The'.
}
}

/** Defines a generic controller including Mvc and AspNetCore */

Check warning

Code scanning / CodeQL

Class QLDoc style. Warning

The QLDoc for a class should start with 'A', 'An', or 'The'.
}
}

/** Find a filter that auto-validates anti-forgery tokens. */

Check warning

Code scanning / CodeQL

Class QLDoc style. Warning

The QLDoc for a class should start with 'A', 'An', or 'The'.
}
}

/** Accounts for custom auto-validation classes with a similar name */

Check warning

Code scanning / CodeQL

Class QLDoc style. Warning

The QLDoc for a class should start with 'A', 'An', or 'The'.
@aegilops
Copy link
Contributor Author

I should expand this to cover other definitely state-changing HTTP verbs, such as DELETE and PUT - I think I kept it at just POST to reflect the existing one.

This will not cover CSRF with GET requests, since that would require detecting state-changing GET requests, which requires analysis of the side-effects of the method that handles the request, and is much more involved.

@michaelnebel michaelnebel self-requested a review July 16, 2024 07:27
Copy link
Contributor

@michaelnebel michaelnebel left a comment

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 - it would be really great if we can get ASP.NET coverage for this query as well! Good work!

Some overall questions and comments
(1) The comments about the QL doc need to be addressed.
(2) It looks like the primary “controversial” difference between the new experimental and the existing query is the removal of the assumption about the presence of at least one of the “interesting” attributes before we start to produce alerts. Do you see any reason why the original query wouldn’t benefit from also producing alerts for missing token validation of POST methods in ASP controllers? If not, then maybe we should consider unifying the logic and incorporate the ASP parts into the existing query and re-use this for the experimental query where the only difference is the part about the presence of at least one “interesting” attribute.

@@ -0,0 +1,27 @@
using System.Web.Mvc;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add some more tests.
(1) Since ASP.NET is now covered by this query, then maybe add a test for an ASP like controller (using Microsoft.AspNetCore.Mvc).
(2) Maybe add a test where one of the CSRF attributes is added to the controller (or a super type of the controller). (I know that this is not covered by the test for the original query - but it would be great to cover it as well).
(3) The most complicated logic is in the "global" add filter logic - I think it would be great to add some tests for this as well.

exists(Attribute attribute |
(
attribute instanceof ValidateAntiForgeryTokenAttribute or
attribute instanceof IgnoreAntiforgeryTokenAttribute or
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we allow this? The description of the attribute states that "A filter that skips antiforgery token validation.".

import csharp
import experimental.code.csharp.MissingAntiForgeryTokenValidationAspNetCore

from Method postMethod, Controller c
Copy link
Contributor

Choose a reason for hiding this comment

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

The existing query assumes that if none of the attributes to ensure token validation is identified, other means of validation are performed (contrary to this query). Do you think that was a bad assumption on the original query?

@@ -0,0 +1,19 @@
using System.Web.Mvc;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe an ASP example would make sense instead (this is also what is mentioned in the qlhelp).


/** The ignore anti-forgery token attribute. */
class IgnoreAntiforgeryTokenAttribute extends Attribute {
IgnoreAntiforgeryTokenAttribute() { this.getType().getName() = "IgnoreAntiforgeryTokenAttribute" }
Copy link
Contributor

Choose a reason for hiding this comment

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

Use hasFullyQualifiedName instead.

---
category: minorAnalysis
---
* A "Missing cross-site request forgery token validation" query for AspNetCore (`cs/web/missing-token-validation-aspnetcore`) was added as an `experimental` query.
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe mention that this query also covers MVC.


<p>Although login and authentication methods are not vulnerable to traditional
CSRF attacks, they still need to be protected with a token or other mitigation.
This because an unprotected login page can be used by an attacker to force a
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: This is because

MethodCall addControllers, LambdaExpr lambda, ParameterAccess options, PropertyCall filters,
MethodCall add
|
// "AddMvc", "AddControllersWithViews", "AddMvcCore", "AddControllers", "AddRazorPages", so generalised to "Add*" to future-proof
Copy link
Contributor

Choose a reason for hiding this comment

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

The the second argument of AddRazorPages is of type Action<RazorPageOptions> and RazorPageOptions doesn't have a Filter property.
Should the logic also check that the first parameter to the lambda is of type MvcOptions?

MethodCall addControllers, LambdaExpr lambda, ParameterAccess options, PropertyCall filters,
MethodCall add
|
// "AddMvc", "AddControllersWithViews", "AddMvcCore", "AddControllers", "AddRazorPages", so generalised to "Add*" to future-proof
Copy link
Contributor

Choose a reason for hiding this comment

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

If all the methods are in Microsoft.Extensions.DependencyInjection then maybe we should check the qualifier?

)
or
// Add<AutoValidateAntiforgeryTokenAttribute>()
add.getTarget().getName() = "Add<AutoValidateAntiforgeryTokenAttribute>"
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we also cover the method Add<TFilterType>(Int32)?

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.

None yet

2 participants