我正在尝试使用框架的工具向 ASP.NET Core React SPA 添加一些简单的 CSRF 验证。应用程序本身本质上是一个 create-react-app 设置(一个带有根元素的 index.html,其他所有内容都从捆绑的 JavaScript 加载)。
修补在链接上找到的一些信息,例如this one,我在我的 中设置了以下内容Startup.ConfigureServices:
services.AddAntiforgery(options => options.Cookie.Name = "X-CSRF-TOKEN");
Run Code Online (Sandbox Code Playgroud)
并在我的 Chrome 工具中确认正在设置 cookie。如果我省略了上面的行,cookie 仍然设置了一个部分随机的名称,例如:.AspNetCore.Antiforgery.RAtR0X9F8_w cookie 正在设置中。我还确认,每当我重新启动整个应用程序时,cookie 值都会更新,因此框架会主动设置此 cookie。
在我的 Chrome 工具中观察网络请求,我确认 cookie 正在通过 AJAX 请求发送到服务器。在服务器上放置断点并观察Request.Cookies控制器操作中的值也证实了这一点。
但是,如果我装饰任何此类 AJAX 请求的操作,[ValidateAntiForgeryToken]则响应始终为空 400。
是否有我在某处错过的配置步骤?也许 action 属性找错了地方,我需要使用不同的验证?
我只是检查日志,发现有一个例外:
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException:所需的防伪 cookie“.AspNetCore.Antiforgery.HPE6W9qucDc”不存在。在 Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext) 在 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
这表示您忘记配置 cookie 名称:
Run Code Online (Sandbox Code Playgroud)public void ConfigureServices(IServiceCollection services) { //services.AddAntiforgery(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); // In production, the React files will be served from this directory services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/build"; }); }
所以我只是添加一个配置如下:
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(o => {
o.Cookie.Name = "X-CSRF-TOKEN";
});
// ...
}
Run Code Online (Sandbox Code Playgroud)
现在可以使用了。
此外,如果您想省略 的行services.AddAntiforgery(options => options.Cookie.Name = "X-CSRF-TOKEN");,您可以使用内置antiforgery.GetAndStoreTokens(context)方法发送 cookie:
app.Use(next => context =>
{
if (context.Request.Path == "/")
{
//var tokens = antiforgery.GetTokens(context);
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("X-CSRF-TOKEN", tokens.CookieToken, new CookieOptions { HttpOnly = false });
context.Response.Cookies.Append("X-CSRF-FORM-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false });
}
return next(context);
})
Run Code Online (Sandbox Code Playgroud)
两者都应该按预期工作。
当它建议通过 JS 可读 cookie 发送两个 cookie 时,这里接受的答案是极其不正确的:
// do not do this
context.Response.Cookies.Append("X-CSRF-TOKEN", tokens.CookieToken, new CookieOptions { HttpOnly = false });
context.Response.Cookies.Append("X-CSRF-FORM-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false });
Run Code Online (Sandbox Code Playgroud)
如果您在 JS 可读的 Cookie 中同时发送 Cookie 令牌和请求令牌,那么您就违背了拥有 Cookie 令牌和请求令牌的目的。
使用这两个令牌的目的是确保
请求令牌可确保您实际加载了页面 (example.com/example-page)。想一想:如果您以管理员身份登录 example.com,则来自浏览器(其中 CORS 允许必要属性)的任何请求都可以成功验证基于 Cookie 的 CSRF 验证和您的身份验证。
但是,通过添加请求令牌,您可以确认您的浏览器在提交请求之前实际上也加载了对表单(或至少是网站)的请求。这通常是通过隐藏输入来完成的。这是通过使用 Asp.Net 中的表单标记帮助程序自动完成的。
<form action="/myEndpoint" method="POST">
<input name="__RequestVerificationToken" type="hidden" value="@antiforgery.GetAndStoreTokens(context).RequestToken" />
<button type="submit">Submit</button>
</form>
Run Code Online (Sandbox Code Playgroud)
它也可以设置在任何地方。,window.CSRFRequestToken并手动添加到 POST 请求,如本fetch例所示:
fetch('/myEndpoint', { method: 'POST', headers: { 'X-XSRF-Token': window.myCSRFRequestToken, 'Bearer': window.mySuperSecretBearerToken } };
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,用户通过 OAuth 或其他方式通过不记名令牌登录(不推荐,在浏览器环境中使用仅 HTTP Cookie)。
Cookie 令牌可确保恶意脚本无法泄露您的请求令牌并代表您发送请求。如果没有它,在供应链攻击中,恶意用户可以将您的秘密发送给恶意行为者:
window.addEventListener('load', => sendMySuperSecretInfoToTheShadowRealm(window.CSRFRequestToken, window.mySuperSecretBearerToken));
Run Code Online (Sandbox Code Playgroud)
现在,恶意用户可以使用您的 CSRF 和不记名令牌进行身份验证,从他们想要的任何地方发送请求。但!如果您有好朋友基于 HTTP-only Cookie 的 CSRF 验证,则不会——因为 JavaScript 无法读取 HTTP-only cookie。
Asp.Net 通过设置 Cookie 令牌和请求令牌来组合这些解决方案。因此,当您向 AspNet 发送请求时,您同时发送:
饼干:
Cookies.Append('X-CSRF-Token', @antiforgery.GetAndStoreTokens(context).CookieToken);
Run Code Online (Sandbox Code Playgroud)
以及 aspnet 表单帮助器标记:
<form action="myEndpoint" />
Run Code Online (Sandbox Code Playgroud)
或手动打印令牌:
<form action="myEndpoint" asp-antiforgery="false">
@Html.AntiForgeryToken()
</form>
Run Code Online (Sandbox Code Playgroud)
或手动向您的脚本提供令牌:
window.myCSRFRequestToken = "@antiforgery.GetAndStoreTokens(context).RequestToken)";
fetch('/myEndpoint', { method: 'POST', headers: { 'X-CSRF-Token': window.myCSRFRequestToken };
Run Code Online (Sandbox Code Playgroud)
如果我没有解释清楚,请完整阅读此页:
https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-6.0
在上面的文档中,最后一个示例使用 cookie 来发送请求 cookie。这与这里的答案有着微妙的不同。接受的答案将两个 cookie 作为 Javascript-可读 发送{ HttpOnly = false }。这意味着 JavaScript 可以读取两者,恶意用户可以读取两者并自行制作特殊请求,该请求将根据 Cookie 和请求 CSRF 验证(在 CORS 允许的情况下)进行验证。
文档中,一个是通过 HTTP only cookie 发送的(JS 无法读取,仅用于基于 Cookie 的 CSRF 验证),另一个是通过 HTTP 可读的 cookie 发送。此 HTTP 可读 cookie 必须由 JavaScript 读取并与上述方法之一(表单输入、标头)一起使用,以便验证 CSRF 请求令牌验证。
| 归档时间: |
|
| 查看次数: |
7180 次 |
| 最近记录: |