האיור אינו זמין: סמל ASP.NET Core (מקור התמונה המקורי).
כל הקוד נועד להמחשה ולא הורץ.
מתקפת CSRF מנצלת אישורים שהדפדפן מצרף אוטומטית, כגון עוגיות אימות, כדי לשלוח בקשות לא רצויות ליישום מהימן. עוגיות נשלחות בהתאם לכללי דומיין, נתיב, Secure ו־SameSite, ולא ממש בכל בקשת HTTP. SameSite הוא שכבת הגנה נוספת מועילה, אך עדיין נכון לאמת אסימונים בפעולות משנות־מצב המזוהות בעוגייה. חולשת XSS או אמון רחב מדי במקורות אחרים עלולים להפר את ההנחות לגבי מי יכול לקרוא אסימון.
הדוגמה מתייחסת ליישום ASP.NET Core MVC קיים, הפועל ב־HTTPS ומאמת משתמשים באמצעות עוגייה. שיטות GET, HEAD, OPTIONS ו־TRACE אינן אמורות לשנות את מצב היישום. ל־API המשתמש רק בכותרת Authorization מפורשת עם bearer יש מודל איום שונה ל־CSRF; אם האימות מתבסס על עוגייה, עצם השימוש בשם API אינו מסיר את הסיכון.
רישום שירותים ואימות מובנה
המאפיין AutoValidateAntiforgeryTokenAttribute מאמת בקשות MVC שאינן בשיטות בטוחות, ומדלג על השיטות הבטוחות. גם ValidateAntiForgeryToken תקין: מוסיפים אותו לפעולה שחייבת תמיד לקבל אסימון. ההנחיה המקורית שלא להשתמש בו כלל הייתה שגויה. רישום השירות הוא AddAuthentication; עיבוד הבקשה נעשה באמצעות UseAuthentication. הנפקת האסימון חייבת לראות את הזהות המאומתת.
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
});
builder.Services.AddAuthorization();
builder.Services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
options.Cookie.Name = "__Host-Antiforgery";
options.Cookie.Path = "/";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
});
builder.Services.AddControllersWithViews(options =>
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();קטע האתחול מניח שהיישום כבר מממש כניסה ופעולות מוגנות משלו. יש להתאים את SameSite של העוגיות לזרימת הכניסה; Strict עלול להפריע לזרימות בין אתרים. זו אינה מערכת כניסה מלאה.
הנפקת אסימון הבקשה לאחר האימות
מזריקים לבקר את IAntiforgery — זהו האיות המדויק של הממשק. עוגיית מניעת הזיוף הפנימית נשארת HttpOnly; אסימון הבקשה הנפרד ניתן לקריאה ב־JavaScript מאותו מקור ולהחזרה בכותרת שהוגדרה. מרעננים אותו אחרי כניסה או שינוי זהות. אין לחשוף למקורות לא מהימנים נקודת קצה בין־מקורית שמחזירה אסימונים.
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
[Authorize]
[Route("security/antiforgery")]
public sealed class AntiforgeryController : Controller
{
private readonly IAntiforgery antiforgery;
public AntiforgeryController(IAntiforgery antiforgery)
=> this.antiforgery = antiforgery;
[HttpGet]
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
public IActionResult Get()
{
var tokens = antiforgery.GetAndStoreTokens(HttpContext);
Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken!, new CookieOptions
{
HttpOnly = false,
Secure = true,
SameSite = SameSiteMode.Strict,
Path = "/",
IsEssential = true
});
return NoContent();
}
}שליחת האסימון במפורש
ברירות המחדל של שמות עוגיות וכותרות שונות בין ספריות לקוח וגרסאות. אין להסתמך על השם בלבד. להלן דוגמת קוד JavaScript המוצגת כטקסט, ולא כסקריפט מוטמע; היא מכינה אפשרויות לקריאה הקיימת שלכם לשינוי מצב באותו מקור.
// In your application's same-origin JavaScript bundle, after sign-in:
await fetch("/security/antiforgery", { credentials: "same-origin" });
const tokenCookie = document.cookie.split("; ")
.find(part => part.startsWith("XSRF-TOKEN="));
if (!tokenCookie) throw new Error("Missing antiforgery request token");
const requestToken = decodeURIComponent(tokenCookie.slice("XSRF-TOKEN=".length));
// For the application's existing same-origin mutation request:
const requestOptions = {
method: "POST",
credentials: "same-origin",
headers: { "X-XSRF-TOKEN": requestToken, "Content-Type": "application/json" }
};
// Attach requestOptions and the intended JSON body to that request.
// Do not send this header to a URL controlled by untrusted input.מתי יש הצדקה לאימות ידני
ב־MVC המסננים המובנים בדרך כלל מייתרים מסנן פעולה מותאם אישית. מחוץ למסלול הזה, IAntiforgery.IsRequestValidAsync(HttpContext) היא בדיקה ידנית נתמכת; עבור GET, HEAD, OPTIONS ו־TRACE היא מחזירה true בלי לאמת אסימון. יש לדחות תוצאה של false לפני שינוי מצב. אין ליצור תחום הזרקת תלויות חדש רק כדי לקבל שירות השייך לבקשה הנוכחית. מניעת זיוף אינה הרשאה: פעולות מוגנות חייבות לאכוף גם את הרשאות המשתמש. קטעי הקוד לא הודרו ולא הורצו.
מקורות ראשוניים
- Microsoft ASP.NET Core antiforgery
- IAntiforgery.IsRequestValidAsync
- OWASP CSRF Prevention Cheat Sheet
- Microsoft middleware ordering
המאמר המקורי באנגלית: Add CSRF protection to .NET core (Antiforgery). תאריך הפרסום המקורי: 2022-02-06T09:49:00.018+02:00.