我的MVC应用程序有常见的aplog方法(在web API和常规控制器中)。我想根据调用来自我的应用程序的哪个区域(视图)来授权这些调用。我面临的问题是如何验证aplog调用的来源。
我意识到这并不容易实现,因为aplog调用很容易被欺骗,但由于我完全控制了视图的呈现方式(整页源代码),也许有一种方法可以嵌入防伪类型的令牌,稍后可以验证到URL引用。
身份验证已经被处理,我可以安全地验证调用的身份,唯一的问题是验证调用来自哪个URL(MVC路由)。更具体地说,防止用户能够欺骗aps调用的起源。
我试着创建一个自定义的授权头,并在视图呈现和apache调用之间传递它,这很有效,但仍然很容易被欺骗(因为用户可以从网站的另一部分嗅探头部并重用它们)。最后,我不知道如何安全地验证头部没有被欺骗。唯一想到的是在令牌中编码一些关于原始上下文的信息,并以某种方式根据传入的调用上下文(在一个传入的调用中传递令牌的上下文)验证它。
我看到MVC有防伪令牌功能,但我不确定这是否能解决我的问题。如果是这样,我想知道如何使用它来验证/api/common/update
是从/home/index
和/user/setup
调用的(这两个调用都是有效的)。
同样,我希望有一种方法来验证哪个页面的一个apache调用是来自,用户身份不是问题。
更新
根据@Sarathy的建议,我尝试实现了防伪token。据我所知,这是通过在每个页面上添加一个带有token的隐藏字段,并将其与cookie中的token进行比较来实现的。下面是我实现的自定义action filter属性,它可以进行token验证:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.RequestContext.HttpContext.Request;
var fToken = req.Headers["X-Request-Verification-Token"];
var cookie = req.Cookies[AntiForgeryConfig.CookieName];
var cToken = cookie != null
? cookie.Value
: "null";
log.Info("filter \ntoken:{0} \ncookie:{1}", fToken, cToken);
AntiForgery.Validate(cToken, fToken);
base.OnActionExecuting(filterContext);
}
字符串
然后我的防伪附加数据提供程序看起来像这样:
public class MyAntiForgeryProvider : IAntiForgeryAdditionalDataProvider
{
public string GetAdditionalData(System.Web.HttpContextBase context)
{
var ad = string.Format("{0}-{1}",context.Request.Url, new Random().Next(9999));
log.Info("antiforgery AntiForgeryProvider.GetAdditionalData Request.AdditionalData: {0}", ad);
log.Info("antiforgery AntiForgeryProvider.GetAdditionalData Request.UrlReferrer: {0}", context.Request.UrlReferrer);
return ad;
}
public bool ValidateAdditionalData(System.Web.HttpContextBase context, string additionalData)
{
log.Info("antiforgery AntiForgeryProvider.ValidateAdditionalData Request.Url: {0}", context.Request.Url);
log.Info("antiforgery AntiForgeryProvider.ValidateAdditionalData additionalData: {0}", additionalData);
return true;
}
型
这是可行的,因为我可以看到正确的页面记录在提供商,和防伪打破了w/出令牌。
然而,除非我做错了什么,否则这似乎是微不足道的欺骗。例如,如果我转到pageA并从pageB复制token(只是表单token,甚至没有cookie token),这仍然成功,并且在我的日志中,我看到pageB同时执行来自pageA的appropriate方法
我使用csrf来生成如下的aScore令牌:
public static string MyForgeryToken(this HtmlHelper htmlHelper)
{
var c = htmlHelper.ViewContext.RequestContext.HttpContext.Request.Cookies[AntiForgeryConfig.CookieName];
string cookieToken, formToken;
AntiForgery.GetTokens(c != null ? c.Value : null, out cookieToken, out formToken);
return formToken;
}
型
然后,我在每次调用apache时将表单标记传递回去,并拥有一个自定义的actionfilter属性,在该属性中,我将沿着cookie标记一起读取/验证表单标记
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.RequestContext.HttpContext.Request;
var fToken = req.Headers[GlobalConstants.AntiForgeKey];
var cookie = req.Cookies[AntiForgeryConfig.CookieName];
var cToken = cookie != null
? cookie.Value
: "null";
log.Info("MyAntiForgeryAttribute.OnActionExecuting. \ntoken:{0} \ncookie:{1}", fToken, cToken);
AntiForgery.Validate(cToken, fToken);
型
这一切工作(改变任何关于令牌抛出正确的异常),然后在我的IAntiForgeryAdditionalDataProvider中,我可以看到它认为它正在处理什么。
只要我从另一个视图覆盖csrf标记,它就会认为它是那个视图。我甚至不需要篡改UrlReferrer来破坏它:/
如果我可以强制cookie在每个页面加载时都不同,则可以使用这种方法
4条答案
按热度按时间yeotifhr1#
您在问题中提到您正在寻找防伪令牌功能。
因此,我认为你问的是一个反CSRF解决方案(CSRF=跨站点请求伪造)。
一种方法是将一个真随机数(一次性令牌)呈现到页面中,然后在每个请求中传递它,这可以通过在请求头中添加一个键/值对来完成,然后在后端(即在控制器内部)进行检查。这是一种挑战-响应方法。
正如你提到的,在服务器端代码中,你可以使用
字符串
从请求页面获取。
要从页面的每个客户端AJAX请求传递它,可以使用
型
或者您可以通过使用
型
注意
tokenValue
需要包含网页发送到客户端时web服务器呈现的随机数。我不会为此使用cookie,因为cookie不能保护您免受CSRF攻击-您需要确保请求的页面与呈现的页面相同(因此由Web服务器创建)。同一浏览器窗口中不同选项卡上的页面也可以使用cookie。
详细信息可以在OWASP project页面的OWASP CSRF prevention cheat sheet中找到。
nle07wnf2#
我的快速临时解决方案是使用在每次页面加载时创建的自定义令牌(guid,我在令牌缓存中跟踪),在所有的aplogs调用中作为头传递。另外,我创建了一个原始的url哈希,并将其合并组合到自定义的auth token中。在aplogs方法中,我然后提取哈希,并将其与UrlReferrer哈希进行比较,以确保它没有被篡改。这是不太明显的猜测是怎么回事令牌似乎是不同的每一个页面加载。然而,这是不安全的,因为有足够的努力,网址哈希可以被发现。曝光是有点有限的,因为用户身份不是问题,所以最坏的情况是一个给定的用户将获得写访问网站的另一部分,但只有作为他自己。我的网站是内部的,我是检查每一个动作,这样任何试图发脾气的行为都会很快被发现。
我同时使用jQuery和angular,所以像这样在所有请求中附加token:
字符串
更新
这种方法的缺点是,自定义令牌需要在Web场或应用程序重新启动时持久化。基于@Sarathy的想法,我试图通过利用MVC防伪框架来解决这个问题。基本上添加/删除我的“盐”,让框架管理实际的令牌验证。这样对我来说就少了一点管理。一旦我验证这是有效的,我会发布更多细节。
yhxst69z3#
所以这将是我不喜欢的“你做错了”的答案之一,所以我先道歉。无论如何,从问题和评论中,我建议你以不同的方式处理问题。不要考虑请求来自哪里,而是考虑请求试图做什么。你需要确定用户是否可以做到这一点。
我的猜测是,你的API接口太通用了。从你的例子API“API/common/update”中,我猜你有一个通用的更新API,它可以更新任何东西,你想保护更新数据X的页面只访问数据Y。如果我错了,那就忽略我。:)
所以我的回答是:不要这样做。改变你的API,使它从你想要处理的数据开始:API/dataX API/dataY。然后使用用户角色来适当地保护这些API方法。如果你喜欢的话,在后台你仍然可以有一个通用的更新例程,它对你有用,但是要保持API接口更具体。
如果你真的不想每个表都有一个API,如果它适合你的情况,也许你至少可以有一个保护/管理表的API和一个单独的标准表的API。
此外,如果你的用户可以更新一些dataX,但不能更新其他dataX,那么你必须对你的数据进行某种检查,最好是对某个根对象进行检查,以及你的用户是否被授权查看/使用该根对象。
所以总结一下,避免过于通用的API接口。通过更具体的方式,你可以使用现有的安全工具来帮助你。
祝你好运!
roqulrg34#
我假设你可以使用IAntiForgeryAdditionalDataProvider来实现这一点。
字符串
在App_Start中注册提供程序,如下所示。
型
https://msdn.microsoft.com/en-us/library/system.web.helpers.iantiforgeryadditionaldataprovider(v=vs.111).aspx
希望这对你的剧本有帮助。