【发布时间】:2017-06-13 19:57:30
【问题描述】:
我基本上需要在作为 MVC 应用程序一部分的 Web API 控制器中防止跨站点请求伪造。我对任何想法持开放态度。此时,我有一个 MVC 视图,它使用 ArcGIS for JavaScript API 显示 Esri 地图。用户在地图上创建一条路线,路线和它经过的各种要素可以通过 AJAX POST 保存。视图没有表单。这是因为我发布到服务器的所有数据都在内存中,并且在屏幕上(或隐藏字段)不可见。
所以,我在 MVC 视图中有以下内容来获取防伪令牌:
@functions{
public string GetTokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
我在视图中有这个隐藏的输入,但意识到这很糟糕,因为它同时具有用于 AntiForgery.Validation 的“表单令牌”和 cookie 令牌:
<input type="hidden" id="antiforgeryToken" value="@GetTokenHeaderValue()" />
然后,在一个单独的 JavaScript 文件中(不在视图中的脚本标记中),我正在对我的 Web API 控制器进行 Http POST。这是我将令牌添加到请求标头的地方:
var headers = {};
headers['RequestVerificationToken'] = $("#antiforgeryToken").val();
// Ajax POST to Web API Controller
$.ajax({
async: true,
url: '/api/RouteData',
type: 'POST',
headers: headers,
data: requestData,
dataType: 'json',
error: function (xhr, statusText, errorThrown) {
console.log('Error saving route data! ' + errorThrown);
},
success: function (result) {
}
});
注意:在正文中发布的数据全部存储在自定义 Dojo 小部件内的内存中(因为该页面使用 ArcGIS for JavaScript 显示 Esri 地图)。由于用户没有输入数据,因此页面上没有表单。)
在 Web API 控制器中的服务器端将它们捆绑在一起:
[System.Web.Http.HttpPost]
[ResponseType(typeof(RouteData))]
public async Task<IHttpActionResult> PostRouteData(RouteDataViewModel routeDataVM)
{
try
{
HttpRequestMessage httpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
ValidateRequestHeader(httpRequestMessage);
}
catch (Exception ex)
{
_logger.Log(LogLevel.Error, ex, ex.Message);
throw;
}
// Now that we know user is who they say they are, perform update
}
void ValidateRequestHeader(HttpRequestMessage request)
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}
AntiForgery.Validate 用于验证令牌。
我看过这个SO post,它给了我一些想法,但并没有完全解决我的问题。这个post on ASP.net 也有很多功劳。
这对我来说(我认为)的不同之处在于我的 JavaScript 位于一个单独的文件中,并且无法调用服务器端 Razor 函数来获取防伪令牌,对吧?关于如何在没有表单的情况下防止 CSRF 的任何想法?
【问题讨论】:
标签: javascript jquery ajax asp.net-mvc asp.net-web-api