小程序 csrf_单页应用程序中的现代csrf缓解

小程序 csrf

Cross-Site Request Forgery (or CSRF or XSRF or “sea-surf”) is one of the oldest attacks against web apps. It means that by embedding a form or URL into a malicious site, the attacker can get a request executed on the vulnerable server as if the visitor of their site made it. There are many ways of protecting your applications, with differing levels of complexity. In Single Page Apps (or SPAs) the simplest ones may be the best.

跨站点请求伪造(或CSRF或XSRF或“海上冲浪”)是针对Web应用程序的最古老的攻击之一。 这意味着,通过将表单或URL嵌入恶意站点,攻击者可以在易受攻击的服务器上执行请求,就像其站点的访问者发出了该请求一样。 保护应用程序的方法有很多,其复杂程度各不相同。 在单页应用程序(或SPA)中,最简单的应用程序可能是最好的。

You might think this doesn’t affect you, or that CSRF is dead, but here is how it could hijack your WordPress page (through a membership plugin), your Jenkins (through a Slack plugin) or your router.

您可能会认为这并不影响您,或者CSRF已死 ,但是这就是它可能如何劫持您的WordPress页面(通过成员资格插件),您的Jenkins (通过Slack插件)或路由器

什么是CSRF? (What is CSRF?)

CSRF is an attack against cookie-based authentication. A site is vulnerable if they check the user’s login state based on a cookie with no (or insufficient) additional checks to see where the request originated. It generally works like this:

CSRF是针对基于cookie的身份验证的攻击。 如果站点根据Cookie来检查用户的登录状态而没有(或没有足够的)其他检查来查看请求的起源,则该站点将很容易受到攻击。 它通常是这样的:

  1. The user logs into the vulnerable site as part of their normal use.

    用户作为正常使用的一部分登录到易受攻击的站点。
  2. The site sets up the session cookies in the browser of the user.

    该站点在用户的浏览器中设置会话cookie。
  3. The user visits the malicious site, that has a hidden form embedded.

    用户访问具有嵌入式隐藏表单的恶意站点。
  4. The hidden form submits a POST request to the vulnerable site or an embedded <img> makes a GET request to it.

    隐藏的表单向易受攻击的站点提交POST请求,或者嵌入式<img>向易受攻击的站点发出GET请求。

  5. The browser executes the request sending the saved credentials along.

    浏览器执行发送保存的凭据的请求。
  6. The server sees that the request came from the user, and assumes it’s legitimate and executes it, posting pictures of cats on your social media. (or something way more malicious)

    服务器看到该请求来自用户,并认为该请求是合法的并执行该请求,并在您的社交媒体上发布了猫的图片。 (或更恶意的方式)
Image for post
CSRF explained
CSRF解释

SPA中有何不同? (How is it different in SPAs?)

A Single Page Application (SPA) is a website, that doesn’t do full page reloads and rewrites the page content instead, to provide a smoother user experience. These include sites built with Angular, React, and other popular frameworks.

单页应用程序(SPA)是一个网站,它不会重新加载整个页面,而是重写页面内容,以提供更流畅的用户体验。 这些包括使用Angular,React和其他流行框架构建的网站。

The relevant part to CSRF is that you don’t do top-level navigation, don’t submit forms and you handle your server API through XMLHttpRequst or fetch() (or some library built upon these). This is important because top-level navigation is handled differently by same-site cookies and you can rely on CORS if you choose to move your API to a subdomain, enabling you to protect your site more reliably against CSRF — I will explain these in the next section.

与CSRF相关的部分是,您不进行顶层导航,不提交表单,并且通过XMLHttpRequstfetch() (或基于它们构建的一些库fetch()来处理服务器API。 这很重要,因为同一站点的Cookie对顶层导航的处理方式不同,如果您选择将API移至子域,则可以依靠CORS,从而使您可以更可靠地保护自己的站点免受CSRF的侵害-我将在下一节。

You can use the following methods in non-SPA sites as well, but you have to watch out for these differences.

您也可以在非SPA网站中使用以下方法,但必须注意这些差异。

减轻 (Mitigation)

There are several ways to prevent this type of attack, ranging from simply setting up your cookies the right way to generating tokens and adding them in the headers of every request.

有几种方法可以防止这种类型的攻击,从简单地以正确的方式设置cookie到生成令牌并将其添加到每个请求的标头中。

1.检查浏览器提供的标题 (1. Checking browser provided headers)

As part of CORS support all browsers support sending the Origin header for a cross-origin request (although IE11 doesn’t agree with everyone else on what’s cross-origin), this includes subdomains, too. This header will always be present if the request came from a different origin, meaning you can check it effectively. A similar header is Referer (sic!), that you can check, but it’s unreliable and raises privacy concerns.

作为CORS支持的一部分, 所有浏览器都支持发送跨域请求的Origin标头 (尽管IE11在跨域问题上并不与其他所有人达成共识),这也包括子域。 如果请求来自其他来源,则该标头将始终存在,这意味着您可以有效地对其进行检查。 您可以检查类似的标头,例如Referer(sic!),但这是不可靠的,并引发了隐私问题。

The problem is that the Origin header is not always there for requests on the same origin. To solve this, you can move your API to a subdomain and set up CORS. This way all your requests are cross-site and you are prepared to handle them.

问题在于,对于相同起源的请求,起源标头并不总是存在。 为了解决这个问题,您可以将API移至子域并设置CORS。 这样,您所有的请求都是跨站点的,您已经准备好处理它们。

2.以更安全的方式设置Cookie (2. Setting your cookies up in a more secure way)

You can set up your cookies with the SameSite attribute. It takes the values of None, Lax and Strict, with Lax being the default in Chrome after Chrome 80.

您可以使用SameSite属性设置cookie 。 它采用None,Lax和Strict的值,其中Lax是Chrome 80之后的Chrome中的默认值。

None in this context means insecure.

在这种情况下, 没有一个表示不安全

Lax means that cookies will only be sent for requests on the same origin, including top-level navigation (e.g.: clicking a link to your site or refreshing). Since cookies are sent with top-level navigation, then if the attacker can open a window and there is an endpoint accepting GET requests they can try to abuse it. Lax can be a great solution if you don’t have GET endpoints that they can abuse.

宽松意味着仅针对相同来源的请求发送cookie,包括顶级导航(例如:单击指向您网站的链接或刷新)。 由于cookie是通过顶级导航发送的,因此,如果攻击者可以打开一个窗口并且某个端点接受GET请求,则他们可以尝试滥用它。 如果您没有可滥用的GET端点,那么Lax可能是一个很好的解决方案。

Strict means that the site’s cookies will only be attached to requests that originated on your site. This would be great, but it means that users clicking a link to your site will not be logged in, even if they did so previously. This is isn’t great UX, so this isn’t a great solution on its own.

严格意味着该网站的Cookie将仅附加到源自您网站的请求。 这很好,但是这意味着单击链接到您的网站的用户将无法登录,即使以前已经登录过也是如此。 这不是一个很棒的UX,所以它本身并不是一个很棒的解决方案。

Lax+Strict: if you use multiple cookies, one set to Strict that will be used for sensitive or state-changing operations (e.g.: transferring money, or changing passwords) and a separate one set to Lax that will enable the users to download non-sensitive data. This combines the above two for the best effect, but you will need to manage multiple cookies.

Lax + Strict :如果您使用多个Cookie,则将其中一个设置为Strict进行敏感或状态更改操作(例如:转帐或更改密码),将单独的一组设置为Lax可使用户下载非敏感数据。 这将上述两个方法结合在一起可以达到最佳效果,但是您将需要管理多个cookie。

You can read a more in-depth explanation of this here.

您可以在这里阅读更深入的解释。

3. CSRF代币 (3. CSRF Tokens)

This is the “classic” way of dealing with CSRF: you add a hidden CSRF token input into forms with the value set to the token you generated and saved on the server (or in an HTTP only cookie), so you can later check it on submission. This solves CSRF issues, if well implemented, but it’s more complex and more prone to errors than the above two.

这是处理CSRF的“经典”方式:您将隐藏的CSRF令牌输入添加到表单中,并将其值设置为在服务器(或仅HTTP cookie)中生成并保存的令牌,因此以后可以检查它提交时。 如果实施得当,这可以解决CSRF问题,但是与上述两个问题相比,它更复杂且更容易出错。

You can read more about this method and the attack in general on the OWASP cheatsheet.

您可以在OWASP速查表上了解有关此方法和一般攻击的更多信息。

您应该使用哪个? (Which should You use?)

These are three very different ways of solving the same problem: which one is the best? OWASP advises using tokens as a primary and the other two as defense-in-depth measures. I’d argue that this could (and maybe should) be done simpler in SPAs.

这是解决同一问题的三种非常不同的方法:哪一种是最好的? OWASP建议使用令牌作为主要防御措施,而其他两个则用作深度防御措施。 我认为这可以(也许应该)在SPA中简化。

My recommendations:

我的建议:

  1. Move your API to a subdomain and use CORS for all requests, checking the origin headers. This ensures that the Origin header is present and it’s great for future-proofing your app anyway.

    将您的API移至子域,并对所有请求使用CORS,并检查源标头。 这样可以确保存在Origin标头,并且无论如何对将来的应用程序都非常有用。
  2. If you can afford to not support some browsers, just use SameSite: Strict cookies, and block requests from unsupported browsers.

    如果您负担不起不支持某些浏览器的费用,请使用SameSite:严格的cookie,并阻止来自不受支持的浏览器的请求。

如何设置API子域和CORS (How to set up an API subdomain and CORS)

Luckily for SPAs, it’s very simple to decouple your API servers and move them to a different subdomain. Likely, you’re already doing some kind of prefixing for your API endpoints to separate them from static content, so it would be as simple as moving your API prefix from the path into the domain part and setting up CORS on your server.

幸运的是,对于SPA,将API服务器解耦并将其移至其他子域非常简单。 可能您已经在为API终结点添加某种前缀以将它们与静态内容分开,所以这就像将API前缀从路径移动到域部分并在服务器上设置CORS一样简单。

This way the Origin header is always there and you can check it with certainty as no request will originate from the same origin as the API. This also has the added benefit of making it easy to move your static content to a CDN and being able to scale your backend independently of your frontend. This is supported in all browsers SPAs do and it’s mostly a matter of configuration, that will also double as future-proofing your web app in case it goes big.

这样,Origin标头始终存在,您可以确定地检查它,因为不会有与API相同的源发出请求。 这还具有额外的好处,可以轻松地将静态内容移动到CDN,并且可以独立于前端扩展后端。 SPA所支持的所有浏览器都支持此功能,这主要是配置问题,如果Web应用程序变大,它也可以兼作将来的功能。

  1. Set up a subdomain at your domain provider: most of the time it’s free and You can do it in a few clicks.

    在您的域提供商处设置子域:大多数情况下,它是免费的,您只需单击几下即可。
  2. Configure your app to listen to the new subdomain. This is usually fairly easy and a few simple searches will get you there. For example in node.js with express, you can use express-subdomain. This use-case is their actual example code for the package.

    配置您的应用程序以侦听新的子域。 通常这很容易,只需进行一些简单的搜索就可以到达那里。 例如,在带有express的node.js中,可以使用express-subdomain 。 这个用例是他们实际的软件包示例代码。

  3. Set up CORS for your application. In node.js there is a package called cors, which also does the header checking for you.

    为您的应用程序设置CORS。 在node.js中,有一个名为cors的软件包,该软件包还会为您检查标题。

// Add CORS to your API
const cors = require('cors');
const corsOptions = {
origin: 'https://example.com',
optionsSuccessStatus: 200 // for some legacy browsers
}
apiRouter.use(cors(corsOptions));// Make the API listen on the subdomain
const subdomain = require('express-subdomain');
app.use(subdomain('api', apiRouter));
app.listen(3000);

如何设置严格的cookie (How to set up Strict cookies)

As most SPAs don’t do top-level navigations that complicate SameSite cookies, you could simply use “SameSite: Strict” and be done with it… The only problem is browser support. Using it won’t break in older browsers, but it will not protect them either. It’s not supported by IE11 on Windows 7 or earlier, UC browser and a few others, with a combined market share of less than 10%.

由于大多数SPA都不会执行使SameSite Cookie复杂化的顶级导航,因此您可以简单地使用“ SameSite:严格”并完成操作……唯一的问题是浏览器支持。 使用它不会在较旧的浏览器中损坏,但也不会保护它们。 Windows 7或更早版本的IE11,UC浏览器和其他一些版本不支持此功能,而其市场份额合计不到10%。

If this is OK, you can simply refuse to serve older browsers using a simple user-agent check. This can be unreliable, but if your users are circumventing it they are at least aware of the problem. Parsing user agents is a whole another topic, that is a bit out of the scope of this. For a quick example use express-useragent or use properly configured regexp from browser-list.

如果可以的话,您可以使用简单的用户代理检查来拒绝为旧版浏览器提供服务。 这可能是不可靠的,但是如果您的用户在规避它,他们至少会意识到问题所在。 解析用户代理是另一个主题,完全超出了此范围。 快速示例使用express-useragent或从浏览器列表中使用正确配置的regexp

const useragent = require('express-useragent');
// For setting the cookie
res.cookie('session', sessionToken, { maxAge: 900000, httpOnly: true, secure: true, sameSite: true });// For refusing old browsers
app.use(useragent.express());
app.get('/', function(req, res){
if (!req.isChrome && !req.isFirefox) {
res.status(400).json({error: "BrowserNotSupported"});
}
});

令牌方法的陷阱 (Gotchas of the token method)

While the CSRF Token method offers good protection, it can go wrong in a variety of ways, and errors can stay hidden for a while. This is one of the advantages of the above methods: you mess up your config and your app fails, but it won’t be vulnerable. Even an eagerly permissive user agent filter is more easily testable compared to all the details you need to watch out for if you use tokens.

虽然CSRF令牌方法提供了良好的保护,但它可以通过多种方式出错,并且错误可以隐藏一段时间。 这是上述方法的优点之一:您弄乱了配置并且应用程序失败了,但是它不会受到攻击。 与使用令牌时需要注意的所有详细信息相比,即使是急切允许的用户代理过滤器也更易于测试。

1.登录CSRF (1. Login CSRF)

Most implementations forget to do CSRF protection pre-login. This might not sound like a problem, but if someone can log you into their account without your knowledge the site would likely just hand them a log of everything you did until you noticed something wrong: purchases, search history, etc.

大多数实现都忘记进行CSRF保护预登录。 这听起来似乎不是问题,但是如果有人在您不知情的情况下将您登录到他们的帐户,则该网站很可能会将您所做的一切记录给他们,直到您发现错误:购买,搜索记录等。

See a reported, yet still active login CSRF in New Relic.

New Relic中查看已报告但仍处于活动状态的登录CSRF。

2.脆弱的子域 (2. Vulnerable sub-domains)

Your site might not be vulnerable, but maybe a subdomain is (e.g.: where you serve user content). If so, they can set cookies and bypass tokens if it’s a double submit cookie implementation.

您的站点可能不会受到攻击,但是可能存在一个子域(例如:您在其中提供用户内容的站点)。 如果是这样,如果是双重提交cookie实现,他们可以设置cookie并绕过令牌。

Read how OWASP describes it defeats double submit cookies.

了解 OWASP如何描述它克服了两次提交Cookie的问题。

3.代币泄漏 (3. Leaked tokens)

Tokens may leak from your application, through bad logging setup, injected malicious user content, some bug or another.

通过错误的日志记录设置,注入的恶意用户内容,某些错误或其他,令牌可能会从您的应用程序中泄漏。

See how Facebook leaked tokens.

了解Facebook如何泄漏代币。

4.缺少支票 (4. Missing checks)

Sometimes, somewhere a check goes missing, or someone adds a too permissive exception to the checks to allow some requests through without a token which may have been fine at the time of the original implementation. However it happens, sometimes apps don’t check the CSRF Token. This seems like a dumb mistake, but even big companies make it.

有时,某处缺少检查,或者某人在检查中添加了一个过于宽松的例外,以允许某些请求没有令牌通过,这在原始实现时可能还不错。 无论如何,有时应用程序不检查CSRF令牌。 这似乎是一个愚蠢的错误,但即使是大公司也能做到。

See how WooCommerce missed checks for follow up steps of an import.

查看WooCommerce如何错过检查的后续导入步骤。

5.令牌不绑定到会话/用户 (5. Tokens not tied to the session/user)

This is a fairly common mistake, where you check if the token is valid and not to whom it belongs. Combine this with needing pre-session CSRF Tokens to protect against login CSRF, and you can see where this is getting complicated.

这是一个相当常见的错误,您在其中检查令牌是否有效,而不是令牌所属的令牌。 将其与需要会话前的CSRF令牌结合使用以防止登录CSRF,您可以看到情况变得越来越复杂。

See how it went wrong for PayPal.

看看PayPal怎么了。

TL; DR (TL;DR)

There are simpler ways of protecting your SPA against CSRF attacks than the generally recommended tokens. I recommend two solutions:

与一般推荐的令牌相比,保护SPA免受CSRF攻击的方法更简单。 我推荐两种解决方案:

  1. Move your API to a subdomain and set up CORS. This way all requests are cross-origin, so by setting it up properly you handle CSRF attacks as well.

    将您的API移至子域并设置CORS。 这样,所有请求都是跨源的,因此通过正确设置请求,您也可以处理CSRF攻击。
  2. Set your cookies with SameSite: Strict and refuse serving old browsers. This loses you about 10% of global users, but it might be OK for you.

    使用SameSite设置您的Cookie:严格并拒绝提供旧的浏览器。 这会使您失去大约10%的全球用户,但对您来说可能还可以。

To me both solutions do better than the “classic” token-based solution: instead of building your complicated solution to hack around an age-old problem, you either align your application with how browsers work and make all requests cross-site (like an attack would) and handle it correctly or you ask the browser to fix their behavior, which new browsers will respect.

对我而言,这两种解决方案都比基于“令牌”的“经典”解决方案做得更好:您无需构建复杂的解决方案来解决一个古老的问题,而是可以使应用程序与浏览器的工作方式保持一致,并让所有请求跨站点(例如攻击)并正确处理它,或者您要求浏览器修复其行为,新的浏览器将尊重这些行为。

Ideally, you should do both of the above (and maybe skip refusing old browsers), and maybe implement CSRF tokens just to be safe and only allow requests that pass all your checks. This is what’s called Defense-in-depth and it’s always the best solution, but this isn’t an ideal world where you have all the time you need to implement every safety measure. Use at least one and then do as much as you can.

理想情况下,您应该同时执行上述两项操作(并可能跳过拒绝旧的浏览器),并可能只是为了安全起见而实现CSRF令牌,并且仅允许通过所有检查的请求 。 这就是所谓的“纵深防御”,它始终是最好的解决方案,但这并不是一个理想的世界,在这里,您始终有时间实施每种安全措施。 至少使用一个,然后尽力而为。

翻译自: https://medium.com/tresorit-engineering/modern-csrf-mitigation-in-single-page-applications-695bcb538eec

小程序 csrf

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值