We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
作者:Martin Splitt 译者:前端小智 来源:dev
阿里云双12已开启,新老用户均可参与,2核1G云服务器仅需79元,,更多服务器配置及价格请关注:Hi拼团,或点此了解“云上爆款1折特惠活动”。同时,建议在购买阿里云相关产品前先领取阿里云2000元代金券会更优惠哦。
双12
79元
为了缩小代码量,这里演示部分代码,完全的代码在 Github 上可以得到。
咱们从一个例子开始,假设咱们有一个网站,网址为 http://good.com:8000/public:
http://good.com:8000/public:
app.get('/public', function(req, res) { res.send(JSON.stringify({ message: 'This is public' })); })
咱们还有一个简单的登录功能,用户可以输入一个共享的密匙并设置一个cookie,以将其标识为已验证:
cookie
app.post('/login', function(req, res) { if(req.body.password === 'secret') { req.session.loggedIn = true res.send('You are now logged in!') } else { res.send('Wrong password.') } })
咱们通过 /private获取一些私有数据,就可以通过上面登录状态来做进一步验证。
/private
app.get('/private', function(req, res) { if(req.session.loggedIn === true) { res.send(JSON.stringify({ message: 'THIS IS PRIVATE' })) } else { res.send(JSON.stringify({ message: 'Please login first' })) } })
目前,咱们 API 并不是专门设计,但可以允许其他人从 /public URL 中获取数据。 假设咱们的API位于good.com:300/public上,并且咱们的客户端托管在thirdparty.com上,该客户端可能会运行以下代码:
/public
good.com:300/public
thirdparty.com
fetch('http://good.com:3000/public') .then(response => response.text()) .then((result) => { document.body.textContent = result })
但这在我们的浏览器中不起作用,通过控制的 network 来看看http://thirdparty.com 的请求:
network
http://thirdparty.com
请求成功,但结果不可用。原因可以在控制台找到:
啊哈!咱们缺少Access-Control-Allow-Origin标头。 但是,为什么我们需要它,它有什么用呢?
Access-Control-Allow-Origin
我们在 JS 中得不到响应结果的原因是同源策略。该策略的目的是确保一个网站不能读取对另一个网站的请求的结果,并由浏览器强制执行。出于安全方面的考虑,现在的网页都用cookie来进行身份验证,如果不限制读取,网页B里的恶意脚本代码可以随意模仿真实用户进行操作。
例如: 如果在咱们�在 example.org上,并不会希望该网站向我们的银行网站发出请求,获取咱们的帐户余额和交易。
example.org
同源策略可以防止这种情况的发生。
在这种情况下,“来源”由
来源
example.com
8000
CSRF
请注意,有一类攻击称为CSRF(跨站点请求伪造),它无法通过同源策略来避免。
在CSRF攻击中,攻击者向后台的第三方页面发出请求,例如向咱们的银行网站发送POST请求。如果我们与我们的银行存在一个有效的会话,任何网站都可以在后台发出请求,该请求将被执行,除非咱们的银行网站有针对CSRF的反措施。
POST
注意,尽管同源策略已经生效,但是的咱们的示例请求从thirdparty.com成功请求到good.com,只是我们无法获得结果。但对于CSRF来说,不需要获取的结果。
good.com
例如,有个 API 通过POST请求方式发送邮件,返回的内容是咱们需要关心的,蛤攻击者不在乎结果,他们关心的是电子邮件是否有发送了成功。
API
现在,咱们希望允许第三方站点(如thirdparty.com)上的 JS 访问咱们的 API 能得到响应。为此,我们可以根据错误提示启用CORS标头:
CORS
app.get('/public', function(req, res) { res.set('Access-Control-Allow-Origin', '*') res.send(...) })
这里将access-control-allow-origin标头设置为*,这意味着:允许任何主机访问此URL和获取响应的结果:
access-control-allow-origin
*
如果请求不是简单请求,浏览器会先发送一个预请求:
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
前面的例子是一个的简单请求。简单的请求是带有一些允许的标头和标志头值的GET或POST请求。现在,对 thirdparty.com 进行了一些更改让它能获取到JSON格式的数据。
GET
JSON
fetch('http://good.com:3000/public', { headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then((result) => { document.body.textContent = result.message })
但这又让thirdparty.com崩溃了,network面板向我们展示了原因:
浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的,头信息里面,关键字段是Origin,表示请求来自哪个源。除了Origin字段,"预检"请求的头信息包括两个特殊字段。
OPTIONS
Origin
(1) Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是GET。
(2) Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段.
此机制允许web服务器决定是否允许实际请求。浏览器设置Access-Control-Request-Headers和Access-Control-Request-Method标头信息,告诉服务器需要什么请求,服务器用相应的标头信息进行响应。
Access-Control-Request-Headers
Access-Control-Request-Method
咱们的服务器还没有响应这些标头信息,所以需要添加它们:
app.get('/public', function(req, res) { res.set('Access-Control-Allow-Origin', '*') res.set('Access-Control-Allow-Methods', 'GET, OPTIONS') res.set('Access-Control-Allow-Headers', 'Content-Type') res.send(JSON.stringify({ message: 'This is public info' })) })
现在,thirdparty.com可以再次获得响应。
现在,假设咱们已登录good.com并可以使用敏感信息访问 /private URL。通过设置CORS,可以让其他网站,比如evil.com获得这些敏感信息,来看看:
evil.com
fetch('http://good.com:3000/private') .then(response => response.text()) .then((result) => { let output = document.createElement('div') output.textContent = result document.body.appendChild(output) })
无论是否已经登录到good.com,都会看到“Please login first”。
Please login first
原因是当请求来自另一个来源时,来自good.com的cookie将不会被发送,在本例中为evil.com。咱们可以要求浏览器发送cookie,即使它是一个跨域源:
fetch('http://good.com:3000/private', { credentials: 'include' }) .then(response => response.text()) .then((result) => { let output = document.createElement('div') output.textContent = result document.body.appendChild(output) })
但同样,这无法在浏览器中工作,其实,这也是个好事。
象一下,任何网站都可以发出经过身份验证的请求,但不会发送实际的cookie,并且无法获得响应。
因此,咱们不希望evil.com能够访问此私有数据-但是,如果我们希望thirdparty.com可以访问/ private,该怎么办?
/ private
在这种情况下,需要将Access-Control-Allow-Credentials标头设置为true:
Access-Control-Allow-Credentials
true
app.get('/private', function(req, res) { res.set('Access-Control-Allow-Origin', '*') res.set('Access-Control-Allow-Credentials', 'true') if(req.session.loggedIn === true) { res.send('THIS IS THE SECRET') } else { res.send('Please login first') } })
但这仍然行不通,允许每个经过身份验证的跨源请求是一种危险的做法。
当咱们希望允许thirdparty.com访问/private时,可以在标头中指定此来源:
app.get('/private', function(req, res) { res.set('Access-Control-Allow-Origin', 'http://thirdparty.com:8000') res.set('Access-Control-Allow-Credentials', 'true') if(req.session.loggedIn === true) { res.send('THIS IS THE SECRET') } else { res.send('Please login first') } })
现在,http://thirdparty:8000也可以访问私有数据,而evil.com被锁定了。
,http://thirdparty:8000
现在,咱们已经允许一个源使用身份验证数据进行跨源请求。但是如果多个第三方来源要怎么办呢?
在这种情况下,可以使用白名单:
const ALLOWED_ORIGINS = [ 'http://anotherthirdparty.com:8000', 'http://thirdparty.com:8000' ] app.get('/private', function(req, res) { if(ALLOWED_ORIGINS.indexOf(req.headers.origin) > -1) { res.set('Access-Control-Allow-Credentials', 'true') res.set('Access-Control-Allow-Origin', req.headers.origin) } else { // allow other origins to make unauthenticated CORS requests res.set('Access-Control-Allow-Origin', '*') } // let caches know that the response depends on the origin res.set('Vary', 'Origin'); if(req.session.loggedIn === true) { res.send('THIS IS THE SECRET') } else { res.send('Please login first') } })
再次提醒:不要直接发送req.headers.origin作为CORS原始标头。这将允许任何网站访问对咱们的网站进行身份验证的请求。
req.headers.origin
这条规则可能有例外,但是在使用没有白名单的凭证实现CORS之前至少要三思。
在本文中,咱们研究了同源策略以及如何在需要时使用CORS来允许跨源请求。
这需要服务器和客户端设置,并且根据请求会出现预检请求。
处理经过身份验证的跨域请求时,应格外小心。 白名单可以帮助允许多个来源,而不会冒泄露敏感数据(在身份验证后受到保护)的风险。
编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:https://dev.to/g33konaut/understanding-cors-aaf
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
https://github.com/qq449245884/xiaozhi
因为篇幅的限制,今天的分享只到这里。如果大家想了解更多的内容的话,可以去扫一扫每篇文章最下面的二维码,然后关注咱们的微信公众号,了解更多的资讯和有价值的内容。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
阿里云
双12
已开启,新老用户均可参与,2核1G云服务器仅需79元
,,更多服务器配置及价格请关注:Hi拼团,或点此了解“云上爆款1折特惠活动”。同时,建议在购买阿里云相关产品前先领取阿里云2000元代金券会更优惠哦。知识要点
事例讲解
为了缩小代码量,这里演示部分代码,完全的代码在 Github 上可以得到。
咱们从一个例子开始,假设咱们有一个网站,网址为
http://good.com:8000/public:
咱们还有一个简单的登录功能,用户可以输入一个共享的密匙并设置一个
cookie
,以将其标识为已验证:咱们通过
/private
获取一些私有数据,就可以通过上面登录状态来做进一步验证。通过 AJAX 从其他域请求咱们的 API
目前,咱们 API 并不是专门设计,但可以允许其他人从
/public
URL 中获取数据。 假设咱们的API位于good.com:300/public
上,并且咱们的客户端托管在thirdparty.com
上,该客户端可能会运行以下代码:但这在我们的浏览器中不起作用,通过控制的
network
来看看http://thirdparty.com
的请求:请求成功,但结果不可用。原因可以在控制台找到:
啊哈!咱们缺少
Access-Control-Allow-Origin
标头。 但是,为什么我们需要它,它有什么用呢?同源策略
我们在 JS 中得不到响应结果的原因是同源策略。该策略的目的是确保一个网站不能读取对另一个网站的请求的结果,并由浏览器强制执行。出于安全方面的考虑,现在的网页都用
cookie
来进行身份验证,如果不限制读取,网页B里的恶意脚本代码可以随意模仿真实用户进行操作。例如: 如果在咱们�在
example.org
上,并不会希望该网站向我们的银行网站发出请求,获取咱们的帐户余额和交易。同源策略可以防止这种情况的发生。
在这种情况下,“
来源
”由example.com
)8000
)关于
CSRF
(跨站点请求伪造) 的说明请注意,有一类攻击称为CSRF(跨站点请求伪造),它无法通过同源策略来避免。
在CSRF攻击中,攻击者向后台的第三方页面发出请求,例如向咱们的银行网站发送
POST
请求。如果我们与我们的银行存在一个有效的会话,任何网站都可以在后台发出请求,该请求将被执行,除非咱们的银行网站有针对CSRF
的反措施。注意,尽管同源策略已经生效,但是的咱们的示例请求从
thirdparty.com
成功请求到good.com
,只是我们无法获得结果。但对于CSRF来说,不需要获取的结果。例如,有个
API
通过POST
请求方式发送邮件,返回的内容是咱们需要关心的,蛤攻击者不在乎结果,他们关心的是电子邮件是否有发送了成功。为咱们的 API 启用 CORS
现在,咱们希望允许第三方站点(如
thirdparty.com
)上的 JS 访问咱们的 API 能得到响应。为此,我们可以根据错误提示启用CORS
标头:这里将
access-control-allow-origin
标头设置为*
,这意味着:允许任何主机访问此URL和获取响应的结果:非简单的请求和预检
如果请求不是简单请求,浏览器会先发送一个预请求:
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
前面的例子是一个的简单请求。简单的请求是带有一些允许的标头和标志头值的
GET
或POST
请求。现在,对thirdparty.com
进行了一些更改让它能获取到JSON
格式的数据。但这又让
thirdparty.com
崩溃了,network
面板向我们展示了原因:浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,"预检"请求用的请求方法是
OPTIONS
,表示这个请求是用来询问的,头信息里面,关键字段是Origin
,表示请求来自哪个源。除了Origin
字段,"预检"请求的头信息包括两个特殊字段。(1) Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是
GET
。(2) Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段.
此机制允许web服务器决定是否允许实际请求。浏览器设置
Access-Control-Request-Headers
和Access-Control-Request-Method
标头信息,告诉服务器需要什么请求,服务器用相应的标头信息进行响应。咱们的服务器还没有响应这些标头信息,所以需要添加它们:
现在,
thirdparty.com
可以再次获得响应。凭证(credentials)和 CORS
现在,假设咱们已登录
good.com
并可以使用敏感信息访问/private
URL。通过设置CORS,可以让其他网站,比如evil.com
获得这些敏感信息,来看看:无论是否已经登录到
good.com
,都会看到“Please login first
”。原因是当请求来自另一个来源时,来自
good.com
的cookie
将不会被发送,在本例中为evil.com
。咱们可以要求浏览器发送cookie
,即使它是一个跨域源:但同样,这无法在浏览器中工作,其实,这也是个好事。
象一下,任何网站都可以发出经过身份验证的请求,但不会发送实际的
cookie
,并且无法获得响应。因此,咱们不希望
evil.com
能够访问此私有数据-但是,如果我们希望thirdparty.com
可以访问/ private
,该怎么办?在这种情况下,需要将
Access-Control-Allow-Credentials
标头设置为true
:但这仍然行不通,允许每个经过身份验证的跨源请求是一种危险的做法。
当咱们希望允许
thirdparty.com
访问/private
时,可以在标头中指定此来源:现在
,http://thirdparty:8000
也可以访问私有数据,而evil.com
被锁定了。允许多个来源
现在,咱们已经允许一个源使用身份验证数据进行跨源请求。但是如果多个第三方来源要怎么办呢?
在这种情况下,可以使用白名单:
再次提醒:不要直接发送
req.headers.origin
作为CORS
原始标头。这将允许任何网站访问对咱们的网站进行身份验证的请求。这条规则可能有例外,但是在使用没有白名单的凭证实现
CORS
之前至少要三思。总结
在本文中,咱们研究了同源策略以及如何在需要时使用CORS来允许跨源请求。
这需要服务器和客户端设置,并且根据请求会出现预检请求。
处理经过身份验证的跨域请求时,应格外小心。 白名单可以帮助允许多个来源,而不会冒泄露敏感数据(在身份验证后受到保护)的风险。
编辑中可能存在的bug没法实时知道,事后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:https://dev.to/g33konaut/understanding-cors-aaf
交流
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
因为篇幅的限制,今天的分享只到这里。如果大家想了解更多的内容的话,可以去扫一扫每篇文章最下面的二维码,然后关注咱们的微信公众号,了解更多的资讯和有价值的内容。
The text was updated successfully, but these errors were encountered: