我基本上需要在我的Web API控制器中防止跨站点请求伪造,这是MVC应用程序的一部分。我对任何想法持开放态度。此时,我有一个MVC视图,它使用ArcGIS for JavaScript API显示Esri地图。用户在地图上创建路线,并且可以通过AJAX POST保存其穿过的路线和各种功能。视图没有表单。这是因为我POST到服务器的所有数据都在内存中,而且在屏幕上(或在隐藏字段中)不可见。
所以,我在MVC View中有以下内容来获取防伪令牌:
@functions{
public string GetTokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
我在视图中有这个隐藏的输入,但意识到这很糟糕,因为它有'形式标记'和用于AntiForgery的cookie标记。验证:
<input type="hidden" id="antiforgeryToken" value="@GetTokenHeaderValue()" />
然后,在一个单独的JavaScript文件中(不在View中的脚本标记中),我正在对我的Web API Controller进行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) {
}
});
注意:正文中获得POST的数据全部位于自定义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的任何想法?
答案 0 :(得分:2)
CSRF保护是您应该制定的,以防止跨站点请求伪造。
跨站请求伪造(CSRF)是一种攻击,迫使最终用户在他们当前正在进行身份验证的Web应用程序上执行不需要的操作。 CSRF攻击专门针对状态更改请求,而不是数据被盗,因为攻击者无法查看对伪造请求的响应。在社交工程的帮助下(例如通过电子邮件或聊天发送链接),攻击者可能会欺骗Web应用程序的用户执行攻击者选择的操作。
以下是针对您的Web API的CSRF攻击示例,如果它不受保护:
<form action="http://yourapi.com/api/DeleteAccount">
<input type="text" name="id" />
<input type="submit" />
</form>
通过在受感染的网站上发布此表单,如果您使用cookie身份验证(例如)作为管理帐户登录Web API,则可以从系统中删除帐户。
这是因为当您重定向到您的API时,您的浏览器会传递存储在yourapi.com
域中的Cookie,从而对用户进行身份验证并执行所需的操作。此攻击向量不同于使用承载令牌来验证您的API,因为第三方不知道此令牌。
正如您正确陈述和实施的那样,使用防伪令牌可以防止这种情况,因为生成的令牌会在Web响应中发送到客户端,然后由客户端发回并验证确保它是我们期望的某个地方的允许请求。与承载令牌一样,第三方可能不知道您在服务器请求中发送的令牌。在这方面,您实施的内容绝对正确,并将令牌传递给客户端就是我们期望它的工作方式。
在ASP.NET MVC(无API)中,实现如下:
<form .. />
@Html.AntiForgeryToken()
</form>
..并验证了服务器端:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Action(MyModel model)
{
// ..
}
当您致电@Html.AntiForgeryToken()
时,该方法都会设置一个Cookie(包含Cookie令牌)并将其发送给客户端,并生成<input type="hidden" ..>
标记以发送给客户端。然后将这些发送回并验证服务器端。
有了这些知识,您可以看到您对发送&#34; cookie令牌&#34;和#34;形成令牌&#34;是没有根据的,因为无论如何都会发生这种情况。
我确实看到你担心的原因,但你试图缓解的似乎是中间人(MitM)攻击而不是CSRF。要解决大部分MitM攻击,您应确保您的站点/ API都通过HTTPS运行。如果您的客户仍然容易受到MitM攻击,那么您的API可能是攻击者最不关心的问题。
答案 1 :(得分:1)
经过与他人的一些讨论后,看来我实施的解决方案实际上是正常的。是的,两个令牌都在隐藏的输入字段中。但是,根据CSRF试图防范的本质 - 另一个 网站尝试代表您发布 - 这个解决方案运行正常。如果我在我的网站上,经过身份验证并浏览到另一个尝试代表我发布POST的网站,则该网站将没有必要的令牌。