Search code examples
asp.net-corecsrfrazor-pages

Why antiforgery token not added to Razor Pages form with action?


In ASP.NET Core Razor Pages, an antiforgery token is automatically added to a form:

The automatic generation of antiforgery tokens for HTML form elements happens when the tag contains the method="post" attribute and either of the following are true:

  • The action attribute is empty (action="").
  • The action attribute isn't supplied ().

I'm so used to that automatic behaviour, that when a form has an action I forget to manually add a token, and of course the POST will fail with a 400 (and I'll waste lots of time debugging).

What is the reason for this behaviour - why is the token not added when there is an action?

Also, is there some way during development to log more informative errors than POST '/somepage' responded '400' when an antiforgery error occurs? I've stumbled over this gotcha a number of times so I wonder whether I can avoid it in the future.


Solution

  • When you set values for the asp-* (page, action etc) attribute, the tag helper uses the routing system to generate a value for the action attribute which is guaranteed to be a local end point. If you set the action attribute manually, it could point to another site entirely, and the csrf field would be meaningless, so there is no point in rendering it.

    From the FormTagHelper source code

    if (string.IsNullOrEmpty(attributeValue))
    {
        // User is using the FormTagHelper like a normal <form> tag that has an empty or complex IHtmlContent action attribute.
        // e.g. <form action="" method="..."> or <form action="@CustomUrlIHtmlContent" method="...">
    
        if (string.Equals(Method ?? "get", "get", StringComparison.OrdinalIgnoreCase))
        {
            antiforgeryDefault = false;
        }
        else
        {
            // Antiforgery default is already set to true
        }
    }
    else
    {
        // User is likely using the <form> element to submit to another site. Do not send an antiforgery token to unknown sites.
        antiforgeryDefault = false;
    }
    

    So the solution is to use the asp- attributes to set the action.