Anti-Fälschungs-Token ist für Benutzer "" gedacht, aber der aktuelle Benutzer ist "Benutzername".

130

Ich erstelle eine einseitige Anwendung und habe ein Problem mit Anti-Fälschungs-Token.

Ich weiß, warum das Problem auftritt. Ich weiß nur nicht, wie ich es beheben soll.

Ich erhalte die Fehlermeldung, wenn Folgendes passiert:

  1. Nicht angemeldeter Benutzer lädt einen Dialog (mit einem generierten Fälschungsschutz-Token).
  2. Benutzer schließt den Dialog
  3. Benutzer meldet sich an
  4. Der Benutzer öffnet den gleichen Dialog
  5. Der Benutzer sendet das Formular im Dialog

Anti-Fälschungs-Token ist für Benutzer "" gedacht, aber der aktuelle Benutzer ist "Benutzername".

Der Grund dafür ist, dass meine Anwendung zu 100% einseitig ist. Wenn sich ein Benutzer erfolgreich über einen Ajax-Beitrag bei anmeldet, schalte /Account/JsonLoginich einfach die aktuellen Ansichten mit den vom Server zurückgegebenen "authentifizierten Ansichten" aus, lade sie jedoch nicht neu Seite.

Ich weiß, dass dies der Grund ist, denn wenn ich die Seite zwischen den Schritten 3 und 4 einfach neu lade, liegt kein Fehler vor.

Es scheint also, dass @Html.AntiForgeryToken()im geladenen Formular immer noch ein Token für den alten Benutzer zurückgegeben wird, bis die Seite neu geladen wird.

Wie kann ich ändern @Html.AntiForgeryToken(), um ein Token für den neuen, authentifizierten Benutzer zurückzugeben?

Ich füge jedem einen neuen GenericalPrincipalmit einem benutzerdefinierten IIdentityWert hinzu Application_AuthenticateRequest, wenn der @Html.AntiForgeryToken()Aufruf HttpContext.Current.User.Identityerfolgt. Tatsächlich ist meine benutzerdefinierte Identität mit der IsAuthenticatedEigenschaft true festgelegt und @Html.AntiForgeryTokenscheint dennoch ein Token für den alten Benutzer zu rendern, es sei denn, ich lade die Seite neu.

Parlament
quelle
Können Sie tatsächlich überprüfen, ob der Code @ Html.AntiForgeryToken aufgerufen wird, ohne ihn neu zu laden?
Kyle C
Es ist definitiv, ich kann dort erfolgreich brechen, um HttpContext.Current.User Objekt zu inspizieren, wie ich erwähnt habe
Parlament
2
Bitte beziehen Sie sich auf diese: stackoverflow.com/a/19471680/193634
Rosdi Kasim
@parliament Könnten Sie bitte in der Antwort unten angeben, für welche Option Sie sich entschieden haben?
Siddharth Pandey
Ich glaube, ich habe eine Ausnahme gemacht, um mit einem vollständigen Nachladen zu gehen, wenn ich mich richtig erinnere. Ich gehe jedoch davon aus, dass dieses Problem in einem neuen Projekt sehr bald auftreten wird. Werde zurückschicken, wenn ich mich für eine bessere Arbeitsoption entscheide.
Parlament

Antworten:

170

Dies geschieht, weil das Anti-Fälschungs-Token den Benutzernamen des Benutzers zur besseren Validierung als Teil des verschlüsselten Tokens einbettet. Wenn Sie @Html.AntiForgeryToken()den Benutzer zum ersten Mal anrufen, ist er nicht angemeldet, sodass das Token eine leere Zeichenfolge für den Benutzernamen hat. Wenn Sie das Anti-Fälschungs-Token nicht ersetzen, wird die Validierung nicht bestanden, da das ursprüngliche Token für war anonymer Benutzer und jetzt haben wir einen authentifizierten Benutzer mit einem bekannten Benutzernamen.

Sie haben einige Möglichkeiten, um dieses Problem zu lösen:

  1. Lassen Sie Ihr SPA diesmal einen vollständigen POST durchführen. Wenn die Seite neu geladen wird, wird ein Fälschungsschutz-Token mit dem aktualisierten, eingebetteten Benutzernamen angezeigt.

  2. Machen Sie eine Teilansicht mit direkt @Html.AntiForgeryToken()und direkt nach dem Anmelden, führen Sie eine weitere AJAX-Anfrage durch und ersetzen Sie Ihr vorhandenes Fälschungsschutz-Token durch die Antwort auf die Anfrage.

  3. Deaktivieren Sie einfach die Identitätsprüfung, die die Fälschungssicherheitsüberprüfung durchführt. Fügen Sie Ihrer Application_Start- Methode Folgendes hinzu : AntiForgeryConfig.SuppressIdentityHeuristicChecks = true.

Epignosisx
quelle
21
@parliament: Sie haben diese Antwort akzeptiert. Können Sie uns mitteilen, welche Option Sie gewählt haben?
R. Schreurs
9
+1 für die nette und einfache Option 3. Zeitgesteuerte Abmeldungen von OAuth-Anbietern verursachen ebenfalls dieses Problem.
Gone Coding
18
Option 3 hat bei mir nicht funktioniert. Während ich abgemeldet war, öffnete ich zwei Fenster auf der Anmeldeseite. In einem Fenster als ein Benutzer angemeldet, dann in dem anderen als ein anderer Benutzer angemeldet und der gleiche Fehler erhalten.
McGaz
5
Leider konnte ich dafür keine gute Lösung finden. Ich habe das Token von der Anmeldeseite entfernt. Ich füge es immer noch in Posts nach dem Login ein.
McGaz
7
Option 3 hat auch bei mir nicht funktioniert. Immer noch der gleiche Fehler.
Joao Leme
25

Um den Fehler zu beheben, müssen Sie die OutputCacheDatenanmerkung auf der Seite Get ActionResultof Login wie folgt platzieren:

[OutputCache(NoStore=true, Duration = 0, VaryByParam= "None")] 
public ActionResult Login(string returnUrl)
user3401354
quelle
3
Dies hat das Problem für mich gelöst, macht total Sinn. Vielen Dank!
Prime03
Mein Anwendungsfall war, dass der Benutzer versucht hat, sich anzumelden, und über ModelState.AddError () ein Fehler angezeigt wurde, z. B. "Konto deaktiviert". Wenn sie dann erneut auf Anmelden klicken, wird dieser Fehler angezeigt. Dieses Update gab ihnen jedoch nur eine leere neue Anmeldeansicht und nicht den Anti-Fälschungs-Token-Fehler. Also keine Lösung.
Ihr öffentlicher Anzeigename
Mein Fall: 1. Benutzer LogIn () und landet auf der Homepage. 2. Der Benutzer drückt die Zurück-Taste und kehrt zur Anmeldeansicht zurück. 3. Melden Sie sich erneut an und sehen Sie den Fehler "Anti-Fälschungs-Token ist für Benutzer bestimmt", aber der aktuelle Benutzer ist "Benutzername". "Auf der Fehlerseite Wenn der Benutzer auf andere Registerkarten im Menü klickt, hat die Anwendung wie erwartet funktioniert . Mit dem obigen Code kann der Benutzer weiterhin auf die Schaltfläche "Zurück" klicken, diese wird jedoch zur Startseite weitergeleitet. Unabhängig davon, wie oft der Benutzer auf die Schaltfläche "Zurück" drückt, wird er auf die Startseite umgeleitet. Vielen Dank
Ravi
Irgendwelche Ideen, warum dies in einer Xamarin-Webansicht nicht funktioniert?
Noobie3001
1
Eine vollständige Erklärung finden Sie unter Verbesserung der Leistung durch Ausgabe-Caching
Stomy
15

Es kommt oft mit meiner Bewerbung vor, deshalb habe ich mich für Google entschieden!

Ich habe eine einfache Erklärung für diesen Fehler gefunden! Der Benutzer doppelklickt auf die Schaltfläche zum Anmelden! Sie können einen anderen Benutzer sehen, der über den folgenden Link darüber spricht:

Das von MVC 4 bereitgestellte Anti-Fälschungs-Token war für den Benutzer "" gedacht, der aktuelle Benutzer ist jedoch "Benutzer".

Ich hoffe, es hilft! =)

Ricardo França
quelle
Das war mein Problem. Vielen Dank!
Nanou Ponette
8

Ich hatte das gleiche Problem und dieser schmutzige Hack hat es behoben, zumindest bis ich es sauberer beheben kann.

    public ActionResult Login(string returnUrl)
    {
        if (AuthenticationManager.User.Identity.IsAuthenticated)
        {
            AuthenticationManager.SignOut();
            return RedirectToAction("Login");
        }

...

Mnemonik
quelle
1
Scheint, als hätte ich das gleiche Problem. IMO, es ist kein Hack, es ist eher eine Sache, auf die wir alle beim Anmelden achten sollten. Wenn der Benutzer bereits angemeldet ist, melden Sie ihn einfach ab und zeigen Sie die Anmeldeseite an. Mein Problem wurde behoben, danke.
Alexandre
7

Die Meldung wird angezeigt, wenn Sie sich anmelden, wenn Sie bereits authentifiziert sind.

Dieser Helfer macht genau das Gleiche wie das [ValidateAntiForgeryToken]Attribut.

System.Web.Helpers.AntiForgery.Validate()

Entfernen Sie das [ValidateAntiForgeryToken]Attribut vom Controller und setzen Sie diesen Helfer in die Aktionsmethode.

Wenn der Benutzer bereits authentifiziert ist, leiten Sie ihn zur Startseite weiter oder fahren Sie nach dieser Überprüfung mit der Überprüfung des gültigen Fälschungsschutz-Tokens fort.

if (User.Identity.IsAuthenticated)
{
    return RedirectToAction("Index", "Home");
}

System.Web.Helpers.AntiForgery.Validate();

Um den Fehler zu reproduzieren, gehen Sie wie folgt vor: Wenn Sie sich auf Ihrer Anmeldeseite befinden und nicht authentifiziert sind. Wenn Sie die Registerkarte duplizieren und sich mit der zweiten Registerkarte anmelden. Und wenn Sie zur ersten Registerkarte auf der Anmeldeseite zurückkehren und versuchen, sich anzumelden, ohne die Seite neu zu laden, tritt dieser Fehler auf.

A. Morel
quelle
Hervorragende Lösung! Dies löste mein Problem, nachdem ich viele andere Vorschläge ausprobiert hatte, die nicht funktionierten. Zunächst einmal war es ein Schmerz, den Fehler zu reproduzieren, bis ich herausfand, dass dies daran liegen könnte, dass zwei Browser oder Registerkarten mit derselben Seite geöffnet wurden und sich der Benutzer von einer Seite aus anmeldete und sich dann von der zweiten Seite anmeldete, ohne sie erneut zu laden.
Nicki
Danke für diese Lösung. Hat auch für mich gearbeitet. Ich habe eine Überprüfung hinzugefügt, um festzustellen, ob die Identität mit dem Anmeldenamen übereinstimmt. In diesem Fall versuche ich weiterhin, den Benutzer anzumelden und ihn abzumelden, wenn dies nicht der Fall ist. Versuchen Sie beispielsweise {System.Web.Helpers.AntiForgery.Validate ();} catch (HttpAntiForgeryException) {if (! User.Identity.IsAuthenticated || string.Compare (User.Identity.Name, model.Username)! = 0) {// Ihre Abmeldelogik hier}}
Steve Owen
2

Ich habe die gleiche Ausnahme, die die meiste Zeit auf dem Produktionsserver auftritt.

Warum passiert das?

Dies geschieht, wenn sich ein Benutzer mit gültigen Anmeldeinformationen anmeldet und einmal angemeldet ist und auf eine andere Seite umleitet. Wenn er auf die Schaltfläche "Zurück" klickt, wird die Anmeldeseite angezeigt, und er hat zu diesem Zeitpunkt erneut gültige Anmeldeinformationen eingegeben.

Wie löst man?

Fügen Sie einfach diese Zeile hinzu und arbeiten Sie perfekt, ohne einen Fehler zu erhalten.

[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
Brijesh Mavani
quelle
1

Ich hatte ein ziemlich spezifisches, aber ähnliches Problem bei der Registrierung. Sobald der Benutzer auf den an ihn gesendeten E-Mail-Link geklickt hat, wird er angemeldet und direkt an einen Bildschirm mit Kontodetails gesendet, um weitere Informationen einzugeben. Mein Code war:

    Dim result = Await UserManager.ConfirmEmailAsync(userId, code)
    If result.Succeeded Then
        Dim appUser = Await UserManager.FindByIdAsync(userId)
        If appUser IsNot Nothing Then
            Dim signInStatus = Await SignInManager.PasswordSignInAsync(appUser.Email, password, True, shouldLockout:=False)
            If signInStatus = SignInStatus.Success Then
                Dim identity = Await UserManager.CreateIdentityAsync(appUser, DefaultAuthenticationTypes.ApplicationCookie)
                AuthenticationManager.SignIn(New AuthenticationProperties With {.IsPersistent = True}, identity)
                Return View("AccountDetails")
            End If
        End If
    End If

Ich habe festgestellt, dass die Rückgabeansicht ("AccountDetails") mir die Token-Ausnahme gab. Ich vermute, dass die ConfirmEmail-Funktion mit AllowAnonymous dekoriert war, die AccountDetails-Funktion jedoch ValidateAntiForgeryToken hatte.

Das Ändern von Return to Return RedirectToAction ("AccountDetails") hat das Problem für mich gelöst.

Liam
quelle
1
[OutputCache(NoStore=true, Duration=0, VaryByParam="None")]

public ActionResult Login(string returnUrl)

Sie können dies testen, indem Sie in der ersten Zeile Ihrer Anmeldeaktion (Abrufen) einen Haltepunkt setzen. Vor dem Hinzufügen der OutputCache-Direktive wird der Haltepunkt beim ersten Laden erreicht, nach dem Klicken auf die Schaltfläche "Zurück" des Browsers jedoch nicht. Nach dem Hinzufügen der Direktive sollte der Haltepunkt jedes Mal erreicht werden, damit das AntiForgeryToken das richtige und nicht das leere ist.

Marian Dalalau
quelle
0

Ich hatte das gleiche Problem mit einer einseitigen ASP.NET MVC Core-Anwendung. Ich habe es behoben, indem ich HttpContext.Useralle Controller-Aktionen festgelegt habe, die die aktuellen Identitätsansprüche ändern (da MVC dies nur für nachfolgende Anforderungen tut, wie hier erläutert ). Ich habe anstelle der Middleware einen Ergebnisfilter verwendet, um die Antiforgery-Cookies an meine Antworten anzuhängen. Dadurch wurde sichergestellt, dass sie erst generiert wurden, nachdem die MVC-Aktion zurückgekehrt war.

Controller (Hinweis: Ich verwalte Benutzer mit ASP.NET Core Identity):

[Authorize]
[ValidateAntiForgeryToken]
public class AccountController : Controller
{
    private SignInManager<IdentityUser> signInManager;
    private UserManager<IdentityUser> userManager;
    private IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory;

    public AccountController(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory)
    {
        this.signInManager = signInManager;
        this.userManager = userManager;
        this.userClaimsPrincipalFactory = userClaimsPrincipalFactory;
    }

    [HttpPost]
    [AllowAnonymous]
    public async Task<IActionResult> Login(string username, string password)
    {
        if (username == null || password == null)
        {
            return BadRequest(); // Alias of 400 response
        }

        var result = await signInManager.PasswordSignInAsync(username, password, false, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            var user = await userManager.FindByNameAsync(username);

            // Must manually set the HttpContext user claims to those of the logged
            // in user. Otherwise MVC will still include a XSRF token for the "null"
            // user and token validation will fail. (MVC appends the correct token for
            // all subsequent reponses but this isn't good enough for a single page
            // app.)
            var principal = await userClaimsPrincipalFactory.CreateAsync(user);
            HttpContext.User = principal;

            return Json(new { username = user.UserName });
        }
        else
        {
            return Unauthorized();
        }
    }

    [HttpPost]
    public async Task<IActionResult> Logout()
    {
        await signInManager.SignOutAsync();

        // Removing identity claims manually from the HttpContext (same reason
        // as why we add them manually in the "login" action).
        HttpContext.User = null;

        return Json(new { result = "success" });
    }
}

Ergebnisfilter zum Anhängen von Antiforgery-Cookies:

public class XSRFCookieFilter : IResultFilter
{
    IAntiforgery antiforgery;

    public XSRFCookieFilter(IAntiforgery antiforgery)
    {
        this.antiforgery = antiforgery;
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var HttpContext = context.HttpContext;
        AntiforgeryTokenSet tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
        HttpContext.Response.Cookies.Append(
            "MyXSRFFieldTokenCookieName",
            tokenSet.RequestToken,
            new CookieOptions() {
                // Cookie needs to be accessible to Javascript so we
                // can append it to request headers in the browser
                HttpOnly = false
            } 
        );
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {

    }
}

Startup.cs extrahieren:

public partial class Startup
{
    public Startup(IHostingEnvironment env)
    {
        //...
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {

        //...

        services.AddAntiforgery(options =>
        {
            options.HeaderName = "MyXSRFFieldTokenHeaderName";
        });


        services.AddMvc(options =>
        {
            options.Filters.Add(typeof(XSRFCookieFilter));
        });

        services.AddScoped<XSRFCookieFilter>();

        //...
    }

    public void Configure(
        IApplicationBuilder app,
        IHostingEnvironment env,
        ILoggerFactory loggerFactory)
    {
        //...
    }
}
Ned Howley
quelle
-2

Hat ein Problem mit der Validierung von Anti-Fälschungs-Token im Internet-Shop: Benutzer öffnen viele Registerkarten (mit Waren) und versuchen, sich nach dem Anmelden bei einem anderen anzumelden, und erhalten eine solche AntiForgeryException. Also, AntiForgeryConfig.SuppressIdentityHeuristicChecks = true hat mir nicht geholfen, also habe ich solch einen hässlichen Hackfix verwendet, vielleicht ist es für jemanden hilfreich:

   public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext exceptionContext)
    {
        var exception = exceptionContext.Exception;

        var request = HttpContext.Current.Request;
        if (request != null)
        {
            if (exception is HttpAntiForgeryException &&
                exception.Message.ToLower().StartsWith("the provided anti-forgery token was meant for user \"\", but the current user is"))
            {
                var isAjaxCall = string.Equals("XMLHttpRequest", request.Headers["x-requested-with"], StringComparison.OrdinalIgnoreCase);
                var returnUrl = !string.IsNullOrWhiteSpace(request["returnUrl"]) ? request["returnUrl"] : "/";
                var response = HttpContext.Current.Response;

                if (isAjaxCall)
                {
                    response.Clear();
                    response.StatusCode = 200;
                    response.ContentType = "application/json; charset=utf-8";
                    response.Write(JsonConvert.SerializeObject(new { success = 1, returnUrl = returnUrl }));
                    response.End();
                }
                else
                {
                    response.StatusCode = 200;
                    response.Redirect(returnUrl);
                }
            }
        }


        ExceptionHandler.HandleException(exception);
    }
}

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new ExceptionPublisherExceptionFilter());
        filters.Add(new HandleErrorAttribute());
    }
}

Ich denke, es ist großartig, wenn Optionen zur Generierung von Anti-Fälschungs-Token festgelegt werden können, um Benutzernamen oder ähnliches auszuschließen.

user3364244
quelle
12
Dies ist ein schreckliches Beispiel für den Umgang mit dem Problem in der Frage. Benutze das nicht.
xxbbcc
Stimme voll und ganz xxbbcc zu.
Javier
OK, Anwendungsfall: Anmeldeformular mit Fälschungsschutz. Öffnen Sie es in 2 Browser-Registerkarten. Melden Sie sich zuerst an. Sie kippt zweiten Registerkarte aktualisieren. Welche Lösung schlagen Sie vor, um ein korrektes Verhalten für Benutzer zu erzielen, die versuchen, sich über die zweite Registerkarte anzumelden?
user3364244
@ user3364244: Richtiges Verhalten kann Folgendes bewirken: Erkennen eines externen Logins mithilfe von Websockets oder signalR. Dies ist die gleiche Sitzung, damit Sie es zum
Laufen