39

前端隐秘角落 - web安全的演变历程

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzU0OTExNzYwNg%3D%3D&%3Bmid=2247487009&%3Bidx=1&%3Bsn=0a50da444fa5dfd2f7536b525c4441b6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

iyeUz26.png!mobile

前言:隐秘的角落番外

这是一个平行时空,在这里,朱朝阳还是有写日记的习惯,不是在日记本,却是在qq中记录。这一天,在他身上发生了一个奇怪的事情:当他像往常一样登录自己的qq空间,却发现最新一条的说说是一个六峰山旅游的广告,但这并不是他写的……这条说说是怎么来的?针对这个问题,他去请教了自己的程序员舅舅张东升。

张东升摸了摸自己的秃头:”你这是被csrf攻击了!“

“接下来我带你来走进web发展史,了解下web安全一路走来的演变历程...”

EFveemU.png!mobile

这篇文章主要围绕web页面安全展开分析:从cookie的源起,到同源策略的出现,再到跨域的兴起,从而引申出csrf攻击。

浏览器的诞生:

追溯到1989年3月12日,万维网 (WWW, World Wide Web) 诞生的日子,蒂姆·伯纳斯-李爵士设计发明了第一个浏览器,架设了第一个 web 服务器 http://info.cern.ch。(详细的介绍在之前文章【http请回答】中有系统的介绍过,可翻阅)而浏览器web的设计之初是知识的共享,所以开放是基本理念。

Cookie

源起:

早期互联网只是用于简单的浏览文档信息、查看门户网站等等,并没有交互这个说法。随着互联网快速发展,交互式web开始兴起,如用户登录,购买商品,各种论坛等。怎么记录用户的操作行为呢?

于是出现了 隐藏域 , 把用户上一次操作记录放在form表单的input中,通过表单提交记录用户操作行为。但是每次都得创建隐藏域而且得赋值太麻烦,且易出错。

// 隐藏域写法
<input type="hidden" name="field_name" value="value">

Cookie最早是网景公司的前雇员Lou Montulli(卢-蒙特利)在1993年3月的发明,并于1994年将“cookies”的概念应用于网络通信。Cookie的出现是为了优化交互式web,同时也规避了隐藏域操作复杂的问题。

定义:

Cookie是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息,用于服务器记录客户端的状态。

zuYJju6.png!mobile

安全性:

cookie在行使自身使命的同时,也存在了安全隐患。

设想一下,你登录了银行系统,并把登录信息、账号密码存在了cookie中,cookie信息共享,大家的账号和密码也共享了,奋斗了一辈子的财富,被别人轻易的转走,将是多么可怕的事情。所以,为了维护互联网的隐私和数据安全,必须制定一定规则。

浏览器安全的基石是"同源政策"(same-origin policy) --- 阮一峰

同源策略

定义:

源是主机,协议,端口名的一个三元组。1995年,同源政策(SOP)由 Netscape 公司引入浏览器,规定了不同域之间访问的策略 协议://域名:端口 。同domain(或ip),同端口,同协议视为同一个域,一个域内的脚本只能读写本域内的资源, 对于其它域的资源,它没有禁止脚本的执行,而是禁止读取HTTP回复, 这种安全限制称为同源策略。

为什么同源限定是三元组?不是二元组?也不是四元组?

2uIfyuY.png!mobile

如果将这个地址组成,比喻为一次快递小哥为合租的你服务的一次行为,那么 协议 则来区分是送快递还是点外卖行为; 域名 则是你的地址, 端口 则是你的房间号, 请求资源地址 则是你需要点的外卖内容,快递小哥根据你的行为和你的地址,房间号来完成这次任务,就算你临时替换了外卖内容,也不影响他完成这次输送任务。

限制范围

  • 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB。

  • 无法接触非同源网页的 DOM。

  • 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)。

同源策略提升了Web前端的安全性,但阻碍了Web拓展的灵活性。 若把html、js、css、flash,image等静态文件全部布置在一台服务器上,会加大这台服务器的压力,甚至威胁到web服务的可用性。 因此,在遵循同源策略的基础上,在安全性和可用性之间选择了一个平衡点。

tips:以下标签是允许跨域加载资源:
* <img src=XXX>
* <link href=XXX>
* <script src=XXX>
* <iframe>

在日益发展的互联网时代,在前端资源和后端数据请求趋向专业化分离的背景下,需要程序员基于同源策略下,打破常规,实现数据请求加载。这就要进行 跨域

跨域

什么是跨域?

由于同源策略的限制,当一个请求 url 的 协议、域名、端口 三者之间任意一个与当前页面 url 不同即为跨域,这是浏览器安全限制下的产物。

Bje6rir.png!mobile

  • 问:跨域只存在于浏览器吗?

  • 答: 是的,这是浏览器的同源策略下的产物,上文中提到,同源策略没有禁止脚本的执行,而是禁止读取HTTP回复。跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。所以,服务端不存在,服务器间的调用,一般是为了获取接口数据的,单一功能。app也相对比较自由,可自行设置白名单等限定条件。

跨域方式

  • 1、 通过jsonp跨域 (get请求)
  • 2、 document.domain + iframe 跨域 (主域相同,子域不同, 设置document.domain为基础主域)
  • 3、 location.hash + iframe 跨域 (不同域之间利用iframe的location.hash传值)
  • 4、 window.name + iframe 跨域 (name值[2MB]在不同的页面加载后依旧存在)
  • 5、 postMessage 跨域 (跨文档通信)
  • 6、 跨域资源共享(CORS) (W3C 标准,跨源ajax请求的根本解决方式)
  • 7、 nodejs中间件代理跨域 (服务器向服务器请求)
  • 8、 nginx代理跨域 (类似于node中间键,使用中转nginx服务器来转发请求)
  • 9、 WebSocket协议跨域 (利用双向通信协议)

这里主要分析下,jsonp 和 cors 跨域方式

JSONP 跨域

  • JSONP是JSON with Padding的略称。它是一个非官方的协议.

上文讲到, <script> 标签是没有被同源策略限制的, jsonp 利用这个漏洞,向网页中动态插入 script 标签,向服务端发送请求,并允许用户传递一个 callback 参数给服务端,然后服务端返回数据时会将这个 callback 参数作为函数名来包裹住JSON数据,这样前端可以随意定制自己的函数来自动处理返回数据了。

// index.html
function jsonp({ url, params, callback }) {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script')
    window[callback] = function(data) {
      resolve(data)
      document.body.removeChild(script)
    }
    params = { ...params, callback } // wd=b&callback=show
    let arrs = []
    for (let key in params) {
      arrs.push(`${key}=${params[key]}`)
    }
    script.src = `${url}?${arrs.join('&')}`
    document.body.appendChild(script)
  })
}
// 调用
jsonp({
  url: 'http://localhost:3000/say',
  params: { wd: 'HowAreYou' },
  callback: 'show'
}).then(data => {
  console.log(data)
})

// server.js
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
  let { wd, callback } = req.query
  console.log(wd) // HowAreYou
  console.log(callback) // show
  res.end(`${callback}('IAmFine')`)
})
app.listen(3000)

jsonp 属于非同源策略(跨域请求)需要服务器支持。又因为 script 标签仅支持get,所以jsonp也只能对get请求跨域,不支持post。因此 jsonp 适合获取资源(只读)

另外,callback参数的传入是在后端进行了一次拼接,存在注入的可能,如果设计不当,是有可能出现安全风险的。

CORS 跨域资源共享

为了更安全的跨域资源访问,于是出现了CORS (Cross-Origin Resource Sharing 跨域资源共享)。它支持所有的请求,包含GET、POST、OPTOIN、PUT、DELETE等,通过对请求头中 Origin 设置,进而实现跨域请求。

浏览器将CORS请求分成两类: 简单请求和非简单请求

baiAryZ.jpg!mobile

只要同时满足以下两大条件,就属于简单请求。

条件一:请求方法是 HEAD/GET/POST 方法之一

条件二:HTTP头部信息不超出以下几种字段:

  • Accept

  • Accept-Language

  • Content-Language

  • Last-Event-ID

  • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

非简单请求会发出一次预检测请求,进行跨域校验,检测完毕才可真正发送请求

EbAZRzA.jpg!mobile

UnYRZvQ.jpg!mobile

CORS 实现跨域:

CORS 需要浏览器和后端同时支持,浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就可以实现跨域。

IfQn22e.jpg!mobile

  • 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS

// 设置支持跨域的源,设置*为任意源,避免使用,必要时可添加多个源
header("Access-Control-Allow-Origin: http://m.zhuanzhuan.com"); 
// 允许携带cookie信息,禁止携带可设置false
header("Access-Control-Allow-Credentials:true"); 
  • 前端设置 withCredentials 字段

// 前端设置是否带cookie
var xhr = new XMLHttpRequest(); 
xhr.withCredentials = true;

安全性

CORS 极大的增强了请求的安全性,但还是会有安全隐患,比如:服务端设置 Access-Control-Allow-Origin = * ;同源策略下对于部分标签的开放非同源限定;浏览器对跨域请求不接收响应也并不拦截请求;在这个背景下,出现了csrf攻击...

CSRF 跨站请求伪造

CSRF(Cross-site request forgery)跨站请求伪造,也被称为:one click attack/session riding。2000年被国外的安全人员提出,2006年开始被国内关注,2008年国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI......而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。

攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

接下来图解下开头的csrf攻击事件

jQJN3mF.jpg!mobile

由此推断出 csrf的攻击特点:

csrf 攻击特点

  • 攻击者借助于受害者的cookie等浏览器信息骗取服务器信任,攻击者拿不到cookie。

  • 由于浏览器同源策略,攻击者拿不到响应结果,只是发起请求。

  • csrf攻击主要是发送修改数据请求。

常见的 csrf 攻击

  • 投票系统,自动投票。

  • 游戏中被迫送礼物。

  • 交易平台,自动收货等

受到csrf攻击,或是因为一个无意的点击,也或是一次随意的浏览,因此,针对以上分析,得出以下防御策略:

CSRF防御

  • 合理的cookie存储

登录信息及过期时间的设定考量,避免攻击方获取后可长时间攻击

  • Get 请求不对数据进行修改;

get 请求只做数据的查询,不做数据增删改的操作,减小被攻击的损失

  • 请求头设置 refere, 验证 HTTP Referer 字段;

页面中请求方的域名限定,杜绝第三方网站的访问

  • 在请求地址中添加 本次操作的唯一标识(sessionId) 并验证;

  • 方案一:sessionId 可以是页面中生成图片验证码、短信验证码,将得到的sessionId传入请求中,并验证,这个方法操作成本有点高,会阻碍用户行为。

  • 方案二: 先通过一个请求实时获取sessionId,将这个值传入请求中并验证。

  • 两种方案都是同样的思路,方案二适用场景要更加广泛些

  • 在 HTTP 头中自定义属性并验证。

属性值需要同时满足以下两点限定条件:

1、前后端要协定,保持一致;

2、每个用户有唯一的值

总结

web安全的历史长河中,从来都不是风平浪静的,每一个技术的出现都有它的使命,每一个技术的陨落也都有它的必然。浏览器诞生,开启了互联网的篇章,到如今互联网功能日益丰富,缺点也暴露的很明显,信息全球化的同时,也要考虑网络中的信息安全与隐私。

参考文章:

  • https://www.ruanyifeng.com/blog/2016/04/cors.html

  • https://segmentfault.com/a/1190000011145364#comment-area

  • https://www.yuque.com/suihangadam/liulanqi/pog4pf

末福

转发本文并留下评论,我们将抽取第 10 名留言者(依据公众号后台排序),转转纪念 T 恤一件或转转随机手办一个,可任选其一,大家快转发起来吧~

zEjAriN.png!mobile

EzURjam.jpg!mobile

UnuEf2F.jpg!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK