为什么同源策略不足以防止 CSRF 攻击?
Why Same-origin policy isn't enough to prevent CSRF attacks?
首先,我假设一个后端控制输入以防止 XSS 漏洞。
在 @Les Hazlewood 中解释了如何在客户端保护 JWT。
Assuming 100% TLS for all communication - both during and at all times
after login - authenticating with username/password via basic
authentication and receiving a JWT in exchange is a valid use case.
This is almost exactly how one of OAuth 2's flows ('password grant')
works.
[...]
You just set the Authorization header:
Authorization: Bearer <JWT value here>
But, that being said, if your REST client is 'untrusted' (e.g.
JavaScript-enabled browser), I wouldn't even do that: any value in the
HTTP response that is accessible via JavaScript - basically any header
value or response body value - could be sniffed and intercepted via
MITM XSS attacks.
It's better to store the JWT value in a secure-only, http-only cookie
(cookie config: setSecure(true), setHttpOnly(true)). This guarantees
that the browser will:
- only ever transmit the cookie over a TLS connection and,
- never make the cookie value available to JavaScript code.
This approach is almost everything you need to do for best-practices
security. The last thing is to ensure that you have CSRF protection on
every HTTP request to ensure that external domains initiating requests
to your site cannot function.
The easiest way to do this is to set a secure only (but NOT http only)
cookie with a random value, e.g. a UUID.
我不明白为什么我们需要具有随机值的cookie来确保向您的站点发起请求的外部域无法正常工作。这不是同源政策免费提供的吗?
来自 OWASP:
Checking The Origin Header
The Origin HTTP Header standard was introduced as a method of
defending against CSRF and other Cross-Domain attacks. Unlike the
referer, the origin will be present in HTTP request that originates
from an HTTPS url.
If the origin header is present, then it should be checked for
consistency.
我知道 OWASP 本身的一般建议是 Synchronizer Token Pattern,但我看不出还有哪些漏洞:
- 安全的 httpOnly cookie 中的 TLS + JWT + 同源策略 + 无 XSS 漏洞。
更新 1:
同源策略仅适用于 ,因此恶意站点可以轻松地发出表单 POST 请求,这会破坏我的安全。需要显式的源头检查。等式为:
- TLS + JWT in secure httpOnly cookie + Origin Header 检查 + 无 XSS 漏洞。
Why Same-origin policy isn't enough to prevent CSRF attacks?
因为Same-origin 策略只适用于读取数据而不适用于写入数据。
您想避免 http://compromised.com
发出这样的请求(从用户的浏览器):
POST https://example.com/transfer-funds
fromAccountId:1
toAccountId:666
一个合法的请求应该是这样的:
POST https://example.com/transfer-funds
fromAccountId: 1
toAccountId: 666
csrfToken: 249f3c20-649b-44de-9866-4ed72170d985
您通过请求一个外部站点无法读取的值(CSRF 令牌)来实现此目的,即在 HTML 表单值或响应 header.
中
关于Origin header,旧的浏览器不支持它,Flash有一些漏洞让客户端改变它。基本上,您会相信 Adobe 以后不会搞砸任何事情……这听起来是个好主意吗?
ensure that you have CSRF protection on every HTTP request
您只需要对具有 side-effects 的请求进行 CSRF 保护,例如更改状态或发送消息
总结
我对 Same-origin 政策和 CORS 的概念有误解,@Bergi、@Neil McGuigan 和@SilverlightFox 帮助我澄清了这一点。
首先,@Bergi 所说的
SOP does not prevent sending requests. It does prevent a page from
accessing results of cross-domain requests.
是一个重要的概念。我认为浏览器不会根据 SOP 限制向跨域发出请求,但这仅适用于 Monsur Hossain 在 this 优秀教程中称为 "not-so-simple requests" 的内容。
Cross-origin requests come in two flavors:
- simple requests
- "not-so-simple requests" (a term I just made up)
Simple requests are requests that meet the following criteria:
- HTTP Method matches (case-sensitive) one of:
- HEAD
- GET
- POST
- HTTP Headers matches (case-insensitive):
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type, but only if the value is one of:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
因此,内容类型为 application/x-www-form-urlencoded 的 POST 将命中服务器(这意味着 CSRF 漏洞),但浏览器将无法访问该请求的结果。
内容类型为 application/json 的 POST 是 "not-so-simple request",因此浏览器将发出这样的预检请求
OPTIONS /endpoint HTTP/1.1
Host: https://server.com
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: https://evilsite.com
Access-Control-Request-Headers: content-type
Accept: */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: es-ES,es;q=0.8
如果服务器响应例如:
Access-Control-Allow-Origin: http://trustedsite.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: content-type
Content-Type: text/html; charset=utf-8
浏览器根本不会发出请求,因为
XMLHttpRequest cannot load http://server.com/endpoint. Response to
preflight request doesn't pass access control check: The
'Access-Control-Allow-Origin' header contains the invalid value
'trustedsite.com'. Origin 'evilsite.com' is therefore not allowed access.
所以我认为 Neil 在指出时是在谈论这个:
the Same-origin Policy only applies to reading data and not
writing it.
然而,有了我向 Bergi 提出的起源 header 明确控制,我认为就此问题而言已经足够了。
关于我对 Neil 的回答,我并不是说那个答案就是我所有问题的答案,但它让我想起了关于 SOP 的另一个重要问题,那就是该政策仅适用于 XMLHTTPRequest。
总之,我认为等式
- 安全 httpOnly cookie 中的 TLS + JWT + 来源 Header 检查 + 无 XSS 漏洞。
如果 API 像 SilverlightFox 所说的那样在另一个域中, 是一个很好的选择。如果客户端与客户端在同一个域中,我将无法处理不包含来源 header 的请求。再次来自 cors tutorial:
The presence of the Origin header does not necessarily mean that the
request is a cross-origin request. While all cross-origin requests
will contain an Origin header, some same-origin requests might have
one as well. For example, Firefox doesn't include an Origin header on
same-origin requests. But Chrome and Safari include an Origin header
on same-origin POST/PUT/DELETE requests (same-origin GET requests will
not have an Origin header).
Silverlight 将 this 指向。
剩下的唯一风险是客户端可以欺骗来源 header 以匹配允许的来源,所以我正在寻找的答案实际上是 this
更新: 对于那些观看此 post 的人,我有 doubts 关于是否需要使用 JWT 的来源 header .
等式为:
- TLS + 存储在安全 cookie 中的 JWT + 请求中的 JWT header + 无 XSS 漏洞。
另外,前面的等式有 httpOnly cookie,但如果客户端和服务器位于不同的域(就像今天的许多 SPA 应用程序),这将不起作用,因为 cookie 不会随每个请求一起发送到服务器。因此,您需要访问存储在 cookie 中的 JWT 令牌并将其发送到 header.
我只是想总结一下答案。
- 正如其他提到的 SOP 仅适用于 XmlHttpRequests。这意味着浏览器必须发送
ORIGIN
header 以及通过 XmlHttpRequests 发出的请求。
- 如果您勾选
Chromium
也会在您提交表单时发送 origin
。但是,这并不意味着其他浏览器可以。下图说明了在 Firefox 中发出的两个 post 请求。一个是由 submitting a form and a second one using XHR 制作的。这两个请求都是从 http://hack:3002/changePassword
到 http://bank:3001/chanePassword
提出的。
- 如果请求来自同一域,则允许浏览器不发送
origin
header。因此服务器应该仅在设置了 origin header 时才检查 origin 策略。
结论是:如果您使用 cookie 并且请求到达服务器而没有 origin
header,您无法区分它是通过从另一个域提交表单还是通过 XHR 发出的在同一域内。这就是为什么您需要使用 CSRF 进行额外检查。
TLDR:
只要发送请求(带cookie),就有可能被csrf攻击
SOP(Same-origin-Policy) 仅不允许 cross-origin 读取(嵌入元素除外,例如 <script> <img>
等),但允许 cross-origin写道。
更具体地说,浏览器使用CORS机制获取Cross-Origin资源,有两种情况:
- 简单的请求
- 浏览器将添加
Origin
字段,然后发送到服务器。(发生 csrf)
- 其他(简单请求除外)
- CORS preflight被触发,请求可能没有发送到服务器。(可能发生csrf)
参考
首先,我假设一个后端控制输入以防止 XSS 漏洞。
在
Assuming 100% TLS for all communication - both during and at all times after login - authenticating with username/password via basic authentication and receiving a JWT in exchange is a valid use case. This is almost exactly how one of OAuth 2's flows ('password grant') works. [...]
You just set the Authorization header:
Authorization: Bearer <JWT value here>
But, that being said, if your REST client is 'untrusted' (e.g. JavaScript-enabled browser), I wouldn't even do that: any value in the HTTP response that is accessible via JavaScript - basically any header value or response body value - could be sniffed and intercepted via MITM XSS attacks.
It's better to store the JWT value in a secure-only, http-only cookie (cookie config: setSecure(true), setHttpOnly(true)). This guarantees that the browser will:
- only ever transmit the cookie over a TLS connection and,
- never make the cookie value available to JavaScript code.
This approach is almost everything you need to do for best-practices security. The last thing is to ensure that you have CSRF protection on every HTTP request to ensure that external domains initiating requests to your site cannot function.
The easiest way to do this is to set a secure only (but NOT http only) cookie with a random value, e.g. a UUID.
我不明白为什么我们需要具有随机值的cookie来确保向您的站点发起请求的外部域无法正常工作。这不是同源政策免费提供的吗?
来自 OWASP:
Checking The Origin Header
The Origin HTTP Header standard was introduced as a method of defending against CSRF and other Cross-Domain attacks. Unlike the referer, the origin will be present in HTTP request that originates from an HTTPS url.
If the origin header is present, then it should be checked for consistency.
我知道 OWASP 本身的一般建议是 Synchronizer Token Pattern,但我看不出还有哪些漏洞:
- 安全的 httpOnly cookie 中的 TLS + JWT + 同源策略 + 无 XSS 漏洞。
更新 1: 同源策略仅适用于 ,因此恶意站点可以轻松地发出表单 POST 请求,这会破坏我的安全。需要显式的源头检查。等式为:
- TLS + JWT in secure httpOnly cookie + Origin Header 检查 + 无 XSS 漏洞。
Why Same-origin policy isn't enough to prevent CSRF attacks?
因为Same-origin 策略只适用于读取数据而不适用于写入数据。
您想避免 http://compromised.com
发出这样的请求(从用户的浏览器):
POST https://example.com/transfer-funds
fromAccountId:1
toAccountId:666
一个合法的请求应该是这样的:
POST https://example.com/transfer-funds
fromAccountId: 1
toAccountId: 666
csrfToken: 249f3c20-649b-44de-9866-4ed72170d985
您通过请求一个外部站点无法读取的值(CSRF 令牌)来实现此目的,即在 HTML 表单值或响应 header.
中关于Origin header,旧的浏览器不支持它,Flash有一些漏洞让客户端改变它。基本上,您会相信 Adobe 以后不会搞砸任何事情……这听起来是个好主意吗?
ensure that you have CSRF protection on every HTTP request
您只需要对具有 side-effects 的请求进行 CSRF 保护,例如更改状态或发送消息
总结
我对 Same-origin 政策和 CORS 的概念有误解,@Bergi、@Neil McGuigan 和@SilverlightFox 帮助我澄清了这一点。
首先,@Bergi 所说的
SOP does not prevent sending requests. It does prevent a page from accessing results of cross-domain requests.
是一个重要的概念。我认为浏览器不会根据 SOP 限制向跨域发出请求,但这仅适用于 Monsur Hossain 在 this 优秀教程中称为 "not-so-simple requests" 的内容。
Cross-origin requests come in two flavors:
- simple requests
- "not-so-simple requests" (a term I just made up)
Simple requests are requests that meet the following criteria:
- HTTP Method matches (case-sensitive) one of:
- HEAD
- GET
- POST
- HTTP Headers matches (case-insensitive):
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type, but only if the value is one of:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
因此,内容类型为 application/x-www-form-urlencoded 的 POST 将命中服务器(这意味着 CSRF 漏洞),但浏览器将无法访问该请求的结果。 内容类型为 application/json 的 POST 是 "not-so-simple request",因此浏览器将发出这样的预检请求
OPTIONS /endpoint HTTP/1.1
Host: https://server.com
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: https://evilsite.com
Access-Control-Request-Headers: content-type
Accept: */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: es-ES,es;q=0.8
如果服务器响应例如:
Access-Control-Allow-Origin: http://trustedsite.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: content-type
Content-Type: text/html; charset=utf-8
浏览器根本不会发出请求,因为
XMLHttpRequest cannot load http://server.com/endpoint. Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header contains the invalid value 'trustedsite.com'. Origin 'evilsite.com' is therefore not allowed access.
所以我认为 Neil 在指出时是在谈论这个:
the Same-origin Policy only applies to reading data and not writing it.
然而,有了我向 Bergi 提出的起源 header 明确控制,我认为就此问题而言已经足够了。
关于我对 Neil 的回答,我并不是说那个答案就是我所有问题的答案,但它让我想起了关于 SOP 的另一个重要问题,那就是该政策仅适用于 XMLHTTPRequest。
总之,我认为等式
- 安全 httpOnly cookie 中的 TLS + JWT + 来源 Header 检查 + 无 XSS 漏洞。
是一个很好的选择。如果客户端与客户端在同一个域中,我将无法处理不包含来源 header 的请求。再次来自 cors tutorial:
The presence of the Origin header does not necessarily mean that the request is a cross-origin request. While all cross-origin requests will contain an Origin header, some same-origin requests might have one as well. For example, Firefox doesn't include an Origin header on same-origin requests. But Chrome and Safari include an Origin header on same-origin POST/PUT/DELETE requests (same-origin GET requests will not have an Origin header).
Silverlight 将 this 指向。
剩下的唯一风险是客户端可以欺骗来源 header 以匹配允许的来源,所以我正在寻找的答案实际上是 this
更新: 对于那些观看此 post 的人,我有 doubts 关于是否需要使用 JWT 的来源 header .
等式为:
- TLS + 存储在安全 cookie 中的 JWT + 请求中的 JWT header + 无 XSS 漏洞。
另外,前面的等式有 httpOnly cookie,但如果客户端和服务器位于不同的域(就像今天的许多 SPA 应用程序),这将不起作用,因为 cookie 不会随每个请求一起发送到服务器。因此,您需要访问存储在 cookie 中的 JWT 令牌并将其发送到 header.
我只是想总结一下答案。
- 正如其他提到的 SOP 仅适用于 XmlHttpRequests。这意味着浏览器必须发送
ORIGIN
header 以及通过 XmlHttpRequests 发出的请求。 - 如果您勾选
Chromium
也会在您提交表单时发送origin
。但是,这并不意味着其他浏览器可以。下图说明了在 Firefox 中发出的两个 post 请求。一个是由 submitting a form and a second one using XHR 制作的。这两个请求都是从http://hack:3002/changePassword
到http://bank:3001/chanePassword
提出的。 - 如果请求来自同一域,则允许浏览器不发送
origin
header。因此服务器应该仅在设置了 origin header 时才检查 origin 策略。
结论是:如果您使用 cookie 并且请求到达服务器而没有 origin
header,您无法区分它是通过从另一个域提交表单还是通过 XHR 发出的在同一域内。这就是为什么您需要使用 CSRF 进行额外检查。
TLDR:
只要发送请求(带cookie),就有可能被csrf攻击
SOP(Same-origin-Policy) 仅不允许 cross-origin 读取(嵌入元素除外,例如 <script> <img>
等),但允许 cross-origin写道。
更具体地说,浏览器使用CORS机制获取Cross-Origin资源,有两种情况:
- 简单的请求
- 浏览器将添加
Origin
字段,然后发送到服务器。(发生 csrf)
- 浏览器将添加
- 其他(简单请求除外)
- CORS preflight被触发,请求可能没有发送到服务器。(可能发生csrf)