Token anti pemalsuan dimaksudkan untuk pengguna "" tetapi pengguna saat ini adalah "nama pengguna"

130

Saya sedang membangun satu aplikasi halaman dan mengalami masalah dengan token anti-pemalsuan.

Saya tahu mengapa masalah ini terjadi, saya tidak tahu cara memperbaikinya.

Saya mendapatkan kesalahan saat hal berikut terjadi:

  1. Pengguna yang tidak masuk log dialog (dengan token anti-pemalsuan yang dihasilkan)
  2. Pengguna menutup dialog
  3. Pengguna masuk
  4. Pengguna membuka dialog yang sama
  5. Pengguna mengirimkan formulir dalam dialog

Token anti pemalsuan dimaksudkan untuk pengguna "" tetapi pengguna saat ini adalah "nama pengguna"

Alasan ini terjadi adalah karena aplikasi saya 100% satu halaman, dan ketika seorang pengguna berhasil masuk melalui posting ajax /Account/JsonLogin, saya cukup mengalihkan tampilan saat ini dengan "tampilan terotentikasi" yang dikembalikan dari server tetapi tidak memuat ulang halaman.

Saya tahu ini alasannya karena jika saya memuat ulang halaman dengan mudah antara langkah 3 dan 4, tidak ada kesalahan.

Jadi tampaknya @Html.AntiForgeryToken()dalam bentuk yang dimuat masih mengembalikan token untuk pengguna lama hingga halaman dimuat ulang.

Bagaimana saya bisa berubah @Html.AntiForgeryToken()untuk mengembalikan token untuk pengguna baru yang diautentikasi?

Saya menyuntikkan baru GenericalPrincipaldengan kustom IIdentitypada setiap Application_AuthenticateRequestsehingga pada saat @Html.AntiForgeryToken()dipanggil HttpContext.Current.User.Identity, sebenarnya Identitas kustom saya dengan IsAuthenticatedproperti disetel ke true namun @Html.AntiForgeryTokenmasih tampaknya memberikan token untuk pengguna lama kecuali saya melakukan memuat ulang halaman.

parlemen
sumber
Bisakah Anda benar-benar memverifikasi bahwa kode @ Html.AntiForgeryToken dipanggil tanpa memuat ulang?
Kyle C
Pasti, saya bisa berhasil istirahat di sana untuk memeriksa HttpContext.Current.User objek seperti yang saya sebutkan
parlemen
2
Silakan lihat ini: stackoverflow.com/a/19471680/193634
Rosdi Kasim
@parlemen dapatkah Anda memberi tahu opsi mana yang Anda pilih dalam jawaban di bawah ini.
Siddharth Pandey
Saya percaya saya membuat pengecualian untuk pergi dengan memuat ulang penuh jika saya ingat dengan benar. Tetapi saya berharap untuk menghadapi masalah ini segera dalam proyek baru. Akan dikirim kembali jika saya memilih dengan opsi kerja yang lebih baik.
parlemen

Jawaban:

170

Ini terjadi karena token anti-pemalsuan menyematkan nama pengguna sebagai bagian dari token terenkripsi untuk validasi yang lebih baik. Ketika Anda pertama kali menelepon @Html.AntiForgeryToken()pengguna tidak masuk sehingga token akan memiliki string kosong untuk nama pengguna, setelah pengguna login, jika Anda tidak mengganti token anti-pemalsuan itu tidak akan lulus validasi karena token awal adalah untuk pengguna anonim dan sekarang kami memiliki pengguna yang diautentikasi dengan nama pengguna yang dikenal.

Anda memiliki beberapa opsi untuk menyelesaikan masalah ini:

  1. Hanya kali ini biarkan SPA Anda melakukan POST penuh dan ketika halaman memuatnya akan memiliki token anti-pemalsuan dengan nama pengguna yang diperbarui tertanam.

  2. Dapatkan tampilan sebagian dengan adil @Html.AntiForgeryToken()dan benar setelah masuk, lakukan permintaan AJAX lain dan ganti token anti-pemalsuan yang ada dengan respons dari permintaan tersebut.

  3. Cukup nonaktifkan identitasnya untuk memeriksa validasi anti-pemalsuan yang dilakukan. Menambahkan berikut untuk Anda Application_Start metode: AntiForgeryConfig.SuppressIdentityHeuristicChecks = true.

epignosisx
sumber
21
@parlemen: Anda menerima jawaban ini, dapatkah Anda berbagi dengan kami opsi mana yang Anda pilih?
R. Schreurs
9
+1 untuk opsi yang bagus & sederhana 3. Logout dengan waktu oleh penyedia OAuth juga menyebabkan masalah ini.
Hilang Coding
18
Opsi 3 tidak berhasil untuk saya. Saat keluar, saya membuka dua jendela ke halaman login. Masuk sebagai satu pengguna di satu jendela, lalu masuk sebagai pengguna lain di jendela lain dan menerima kesalahan yang sama.
McGaz
5
Sayangnya, saya tidak bisa mendapatkan solusi yang bagus untuk ini. Saya menghapus token dari halaman login. Saya masih memasukkannya di pos setelah masuk.
McGaz
7
Opsi 3 juga tidak bekerja untuk saya. Masih mendapatkan kesalahan yang sama.
Joao Leme
25

Untuk memperbaiki kesalahan Anda perlu menempatkan OutputCacheAnotasi Data pada halaman Dapatkan ActionResultLogin sebagai:

[OutputCache(NoStore=true, Duration = 0, VaryByParam= "None")] 
public ActionResult Login(string returnUrl)
pengguna3401354
sumber
3
Ini memecahkan masalah bagi saya, sangat masuk akal. Terima kasih!
Perdana03
Kasing penggunaan saya adalah pengguna mencoba login, dan ditampilkan kesalahan misalnya "akun dinonaktifkan" melalui ModelState.AddError (). Kemudian jika mereka mengklik login lagi mereka akan melihat kesalahan ini. Namun, perbaikan ini hanya memberi mereka tampilan login kosong baru lagi daripada kesalahan token anti pemalsuan. Jadi, bukan perbaikan.
yourpublicdisplayname
Kasus Saya: 1. Pengguna Masuk () dan mendarat di halaman beranda. 2. Pengguna menekan tombol kembali dan kembali ke tampilan Login. 3. Pengguna masuk lagi dan melihat kesalahan "Token anti pemalsuan dimaksudkan untuk pengguna" "tetapi pengguna saat ini adalah" nama pengguna "" Pada halaman kesalahan Jika pengguna mengklik tab lain dari menu, aplikasi berfungsi seperti yang diharapkan . Menggunakan kode di atas pengguna masih dapat menekan tombol kembali tetapi akan diarahkan ke halaman rumah. Jadi, tidak peduli berapa kali pengguna menekan tombol kembali, itu akan mengarahkannya ke halaman utama. Terima kasih
Ravi
Adakah gagasan mengapa ini tidak berhasil pada tampilan web Xamarin?
Noobie3001
1
Untuk penjelasan lengkap, lihat meningkatkan kinerja dengan output caching
stomy
8

Saya memiliki masalah yang sama, dan hack kotor ini memperbaikinya, setidaknya sampai saya bisa memperbaikinya dengan cara yang lebih bersih.

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

...

ilmu tentang cara menghafal
sumber
1
Sepertinya saya punya masalah yang sama. IMO itu bukan peretasan, ini lebih dari hal yang umum kita semua harus memeriksa ketika login. Jika pengguna sudah masuk, cukup keluar dan tampilkan halaman login. Atasi masalah saya, terima kasih.
Alexandre
7

Pesan muncul ketika Anda masuk saat Anda sudah diautentikasi.

Pembantu ini melakukan hal yang persis sama dengan [ValidateAntiForgeryToken]atribut.

System.Web.Helpers.AntiForgery.Validate()

Hapus [ValidateAntiForgeryToken]atribut dari controller dan tempatkan helper ini dalam metode aksi.

Jadi ketika pengguna sudah diautentikasi, arahkan kembali ke beranda atau jika tidak melanjutkan verifikasi token anti-pemalsuan yang valid setelah verifikasi ini.

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

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

Untuk mencoba mereproduksi kesalahan, lanjutkan sebagai berikut: Jika Anda berada di halaman login Anda dan Anda tidak diautentikasi. Jika Anda menduplikasi tab dan Anda masuk dengan tab kedua. Dan jika Anda kembali ke tab pertama pada halaman login dan Anda mencoba masuk tanpa memuat ulang halaman ... Anda memiliki kesalahan ini.

A. Morel
sumber
Solusi yang sangat baik! Ini menyelesaikan masalah saya setelah mencoba banyak saran lain yang tidak berhasil. Pertama itu merepotkan kesalahan, sampai saya menemukan bahwa itu bisa karena 2 browser atau tab terbuka dengan halaman yang sama, dan pengguna masuk dari satu, dan kemudian masuk dari yang kedua tanpa memuat ulang.
Nicki
Terima kasih atas solusi ini. Bekerja untuk saya juga. Saya menambahkan tanda centang untuk melihat apakah Identitasnya sama dengan nama pengguna login, dan jika demikian saya dengan senang hati terus mencoba untuk login pengguna, dan log out jika tidak. Misalnya, coba {System.Web.Helpers.AntiForgery.Validate ();} catch (HttpAntiForgeryException) {if (! User.Identity.Isententicated || string.Compare (User.Identity.Name, model.Username)! = 0) {// Logout logout Anda di sini}}
Steve Owen
2

Saya memiliki pengecualian yang sama yang terjadi sebagian besar waktu di server produksi.

Mengapa itu terjadi?

Itu terjadi ketika pengguna login dengan kredensial yang valid dan sekali login dan redirect ke halaman lain, dan setelah mereka menekan tombol kembali akan menampilkan halaman login dan lagi ia memasukkan kredensial yang valid bahwa waktu pengecualian ini akan terjadi.

Bagaimana menyelesaikan?

Cukup tambahkan baris ini dan bekerja dengan sempurna, tidak ada kesalahan.

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

Saya memiliki masalah yang cukup spesifik namun serupa dalam proses pendaftaran. Setelah pengguna mengklik tautan email yang dikirimkan kepada mereka, mereka akan login dan dikirim langsung ke layar detail akun untuk mengisi beberapa informasi lebih lanjut. Kode saya adalah:

    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

Saya menemukan bahwa Return View ("AccountDetails") memberi saya pengecualian token, saya menduga karena fungsi ConfirmEmail didekorasi dengan AllowAnonymous tetapi fungsi AccountDetails telah ValidateAntiForgeryToken.

Mengubah Return to Return RedirectToAction ("AccountDetails") memecahkan masalah bagi saya.

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

public ActionResult Login(string returnUrl)

Anda dapat menguji ini dengan meletakkan break point pada baris pertama dari tindakan Login (Get) Anda. Sebelum menambahkan arahan OutputCache breakpoint akan mengenai beban pertama, tetapi setelah mengklik tombol kembali browser itu tidak. Setelah menambahkan arahan Anda harus berakhir dengan breakpoint dipukul setiap waktu, sehingga AntiForgeryToken akan menjadi yang korektif, bukan yang kosong.

Marian Dalalau
sumber
0

Saya memiliki masalah yang sama dengan aplikasi ASP.NET MVC Core satu halaman. Saya mengatasinya dengan menetapkan HttpContext.Userdalam semua tindakan pengontrol yang mengubah klaim identitas saat ini (karena MVC hanya melakukan ini untuk permintaan berikutnya, seperti yang dibahas di sini ). Saya menggunakan filter hasil alih-alih middleware untuk menambahkan cookie antiforgery ke respons saya, yang memastikan bahwa itu hanya dihasilkan setelah tindakan MVC kembali.

Pengontrol (NB. Saya mengelola pengguna dengan 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" });
    }
}

Filter hasil untuk menambahkan cookie antiforgery:

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)
    {

    }
}

Ekstrak Startup.cs:

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
sumber
-3

Punya masalah dengan validasi anti-pemalsuan-token di internet-shop: pengguna membuka banyak tab (dengan barang) dan setelah masuk pada satu mencoba masuk pada yang lain dan mendapat AntiForgeryException tersebut. Jadi, AntiForgeryConfig.SuppressIdentityHeuristicChecks = true tidak membantu saya, jadi saya menggunakan peretasan jelek seperti itu, mungkin itu akan membantu seseorang:

   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());
    }
}

Berpikir itu akan bagus jika opsi generasi anti-pemalsuan-token dapat diatur, untuk mengecualikan nama pengguna atau sesuatu seperti itu.

pengguna3364244
sumber
12
Ini adalah contoh buruk dari berurusan dengan masalah dalam pertanyaan itu. Jangan gunakan ini.
xxbbcc
Sepenuhnya setuju dengan xxbbcc.
Javier
Oke, gunakan case: form login dengan token anti pemalsuan. Buka di 2 tab browser. Masuk dulu. Anda tidak dapat menyegarkan tab kedua. Solusi apa yang Anda sarankan untuk memiliki perilaku yang benar untuk pengguna yang mencoba masuk dari tab kedua?
user3364244
@ user3364244: perilaku yang benar dapat yang berikut: mendeteksi login eksternal dengan menggunakan soket web atau signalR. Ini adalah sesi yang sama sehingga Anda bisa membuatnya berfungsi saya kira :-)
dampee