Hello,AV8D~今天我們要分享的主題是前端Web
安全。 web
安全的重要性不言而喻,是所有網路企業都繞不開的話題。
在web前端領域,儘管瀏覽器已經在系統層面幫我們做了諸多的隔離和保護措施,但是網頁程式碼開放式的特點和html、JS
的語言特性使得駭客們仍然有非常多的可乘之機,Google、facebook
等等都有各自的漏洞懸賞機制,力求在駭客之前發現和修復漏洞,將企業損失降到最低。
web安全是老生常談的話題,但常說常新,今天就再次梳(c)理(v)一遍前端的一些web攻擊手段,讓大家看完這篇文章之後可以學到:
XSS
的全名是Cross-Site Scripting
,跨站腳本攻擊。是指透過利用網頁開發時留下的漏洞,透過巧妙的方法注入惡意指令程式碼到網頁,使用戶載入並執行攻擊者惡意製造的網頁程式。通俗一點講,就是黑客想法設防地讓用戶在訪問的網頁裡運行自己寫的攻擊代碼,一旦成功運行,黑客就有可能乾出以下勾當:
cookies
,從而拿到使用者的敏感資訊;DDos
攻擊;根據攻擊的來源,XSS
可以大體分為持久性攻擊和非持久性攻擊(當然也有分反射型、儲存型和Dom型的,我個人比較願意選擇第一種分類,因為比較容易理解)。
非持久型XSS的特點在於即時性,它不需要儲存在伺服器中,透過巧妙地建構一個帶有惡意程式碼的URL,然後引導使用者點擊訪問,即可實現攻擊。
舉個簡單的小栗子,假設有個網站如下。
細心的你發現這個頁面會把url
上面的q
參數內容放到網頁中,而你打開調試台看到網頁的程式碼實作如下:
<h1 id="query-key"></h1><span>查询结果如下</span>// ...<script> const reg = new RegExp("(^|&)q=([^&]*)(&|$)", "i"); const res = window.location.search.substr(1).match(reg); if (res != null) { const query = decodeURIComponent(res[2]); document.getElementById('query-key').innerHTML = query; };</script>复制代码
發現它沒有對query
做過濾,而且直接用innerHTML
方法插入到了DOM
中,你看完差點笑出聲:一點安全意識都沒有!那假如你想針對這個網站設計一個URL
來讓用戶點擊後發起一個XSS
攻擊,從而拿到用戶的cookies
數據,你會怎麼做呢?
那還不是非常的so easy
,反手就可以寫出一條XSS
的連結出來:
http://abcd.com?q=<script>alert(document.cookie)</script>复制代码
這樣網頁會把q
參數中的script標籤插入到DOM中執行,進而彈出提示框。
思路肯定是沒問題的,不過你可能會發現不起效果,這是因為瀏覽器針對script
等一些危險標籤的插入做了攔截過濾,當然了這難不倒我們,畢竟咱們也不能把我要乾壞事寫在臉上,這不尊重對手,所以咱們換種委婉一點的寫法就行了:
http://abcd.com?q=<img src="" onerror="alert(document.cookie)" />复制代码
因為是插入一張圖片,瀏覽器一般不會過濾攔截,然後我們把src
置空使其觸發onerror
事件,間接地執行我們的惡意腳本。 done!
你可能發現非持久型攻擊有個不好的地方,那就是在攻擊之前你得先讓用戶能夠看到你的這行URL並且誘導他點擊,說白了你還得想辦法提高這個惡意連結的曝光度!這好麻煩啊,那有沒有一勞永逸的方法呢?
假如我們可以想辦法把惡意程式碼放到網站的資料庫裡邊存起來,那接下來這個網站所有訪問到我這個惡意資料的用戶不就都遭殃了嗎?
因此假如某個網站的評論區也未作過濾的安全處理,那麼我們就可以透過評論的方式將惡意腳本提交至後台伺服器,之後任何訪問了此評論頁面的用戶,都會執行到我的程式碼,從而達到攻擊目的。
A用戶在評論裡邊寫入了一條惡意程式碼並提交後台:
当B用户访问这个评论页面时,网页就会加载A的评论,进而触发恶意代码(而此时B用户其实啥都没做,他只是非常乖巧地打开了这个非常正规的网站,然后他就中招了):
因此我们可以总结一下持久型XXS的特点:
介绍完概念,我们可以知道要想抵御XSS攻击,大概可以从两方面入手:
1、使用HTML转义。对外部插入的内容要永远保持警惕。
对所有外部插入的代码都应该做一次转义,将script
,& < > " ' /
等危险字符做过滤和转义替换,同时尽量避免使用innerHTML
,document.write
,outerHTML
,eval
等方法,用安全性更高的textContent
,setAttribute
等方法做替代;
2、开启CSP防护。内容安全策略(CSP)的设计就是为了防御XSS攻击的,通过在HTTP头部中设置Content-Security-Policy
,就可以配置该策略,如果将CSP设置成一下模式:
Content-Security-Policy: script-src 'self'复制代码
那么该网站将:
这将有效地防范XSS的攻击,当然他也非常严格,可能会对自身的业务开发也造成一定限制,更多关于CSP的内容可以查看MDN。
3、设置HttpOnly。当然这已经是属于降低XSS危害的方法,对于所有包含敏感信息的cookie,都应该在服务端对其设置httpOnly
,被设置了httpOnly
的cookie字段无法通过JS获取,也就降低了XSS攻击时用户凭据隐私泄漏的风险。
又是一个耳熟能详,面试常问的知识点。
CSRF(Cross-site request forgery)中文名称跨站请求伪造,攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
简单介绍一下CSRF的攻击流程:
最后在受害者不知情的情况下,恶意网站向目标网站以受害者身份执行了一些带权限的操作,给受害者带来一系列危害和损失。
这个过程很好理解,篇幅有限,这里就不举例子做详细的解释了。
至于浏览器存在跨域限制,那么恶意网站如何对目标网站发起请求的问题,这属于如何解决跨域限制的知识点,在这里可以简单总结为以下方法:
img
标签链接不受浏览器的跨域限制,因此可以直接以img的形式发起请求;知道了整个攻击的流程,那么我们就可以着手看看在哪些阶段可以对CSRF攻击做防御工作。
1、 SameSite Cookie
如果能讓第三方發起的請求無法攜帶相關的cookie,那一切問題好像就解決了啊。值得慶幸的是,瀏覽器針對cookie提供了SameSite
的屬性,該屬性表示 Cookie 不隨著跨域請求發送。該提案有google提出,美中不足的是目前還在試行階段,有相容性問題。
2、CSRF Token
既然瀏覽器暫時無法幫我們解決所有問題,那就自己想辦法把身分校驗機制搞複雜一點,只靠cookie沒有安全保障,那就再加一個校驗維度進來。 SCRF Token就是這樣一種解決方案。
這個方案流程就是:在使用者造訪網站時,後台伺服器根據演算法再產生一個Token,然後把這個Token放在seesion中,當網站發起請求時,不僅要攜帶cookie憑證,還要把這個Token也帶上,後台一起校驗之後確認身分再執行操作。由於SCRF Token是放在session中,因此當第三方網站發起請求時,無法拿到這個SCRF Token,故身分校驗不再通過,就達到了防禦攻擊的效果。
3、同源偵測
由於CSRF都是透過三方網站發起,因此我們如果能判斷伺服器每次收到的請求來自哪些網站,就可以過濾那些有安全風險的網站發起的請求,降低被攻擊的風險。
Referer
和Origin
是http請求的頭部欄位之一,用來標誌該請求是從哪個頁面連結過來的。因此後台伺服器可以透過檢查該欄位是否是來自自己的網站鏈接,來避免第三方網站發動CSRF攻擊。但是同源檢測的可靠性並不高,例如在302重定向的時候,為了保護來源,http請求不會攜帶Origin
字段,而Referer
字段會受到Referer Policy規則的限製而不發送。
4、增加二次驗證
針對一些有危險性的請求操作(例如刪除帳號,提現轉帳)我們可以增加用戶的二次,例如發起手機或郵件匣驗證碼檢驗,進而降低CSRF打來的危害。
1)Google Digital Garage是Google的一個線上教育產品,一個國外的安全工程師在他的部落格介紹如何透過CSRF攻擊刪除網站的個人帳號。
2)這篇文章則介紹了facebook網站上的安全漏洞,如何透過CSRF繞過安全保護實現帳戶接管。
相信有些小夥伴已經開始犯困,畢竟前面介紹的兩種Web攻擊手段對很多人來說已經耳熟能詳,如數家珍了。那本文的最後一章再介紹一種可能不太常見,但卻開始重新回歸大眾視野的Web攻擊手段:XS-Leaks。
XS-Leaks即跨站洩漏。 XS-Leaks利用了對HTTP快取進行查詢的機制,透過對資源快取的判斷進而推斷出目前使用者的相關資訊。如果你第一次聽到XS-Leaks,那我相信你一定很驚訝為什麼普普通通的HTTP資源快取也能造成用戶資訊外洩。
Chrome瀏覽器團隊在他們的開發者網站中發表了一篇文章,表示在86版本之後的瀏覽器將開始對請求快取資源機制進行分區(分域名)管理,原因就是因為之前的快取機制可能會造成隱私洩漏。
在這之前Chrome瀏覽器的請求快取策略很簡單:
由於快取資源沒有網域限制,所有網站都共享了快取資源,因此利用這一點就可以偵測使用者是否造訪過特定的網站:惡意網站透過發起特定的資源請求,透過判斷此資源是否來自快取就可以推斷出使用者的瀏覽歷史。例如我在我的網站中請求掘金的LOGO圖片(或其他更具唯一性的資源),如果我判斷出這張圖片來自緩存,那我就可以知道該用戶訪問過掘金網站。
XS-Leaks和上述的原理類似,利用了對HTTP快取進行查詢的機制,透過偵測一個查詢詢問是否有結果來取得一些使用者資料。 XS-Leaks攻擊的主要步驟流程如下:
举一个小栗子,假如我们想知道当前访问我们网站的用户是否是某社交网站(假设链接为http://xxx.com)昵称为@helloWorld
的用户,利用XS-Leaks攻击应该怎么做呢?
首先我们可以先将这名@helloWorld
的用户头像图片从社交网站上扒下来,假设该图片链接长这样: http://xxx.com/user/helloWorld.jpg
。
为了避免用户因为浏览了社交网站的一些页面(比如帖子列表页),请求过helloWorld的头像导致的缓存影响判断目的,当用户访问我们的页面时,我们需要先想办法清空这张头像的浏览器缓存。那么怎么能强制清楚一张图片的缓存呢?
FETCH POST http://xxx.com/user/helloWorld.jpg复制代码
没错,通过发起一个POST请求,就可以清除浏览器对这张图片的缓存(感兴趣的小伙伴可以看看这篇文章)。
接下来通过iframe
或者<link ref=rerender href="http://xxx.com/user" />
等方式悄悄发起一个请求,访问社交网站的个人信息页面http://xxx.com/user
(这个页面包含用户的头像图片)。
紧接着只需要再请求一次头像http://xxx.com/user/helloWorld.jpg
,判断其是否来自缓存,如果是,那不就说明了刚才请求的个人信息页面包含了这张图片,就可以推断出该名用户就是@helloWorld
了。
那么问题又来了:如何判断一个请求来自缓存呢?
方法还是很多的,一种方法是通过读取img的宽高属性,来判断图片是否来自缓存:
const img = new Image(); img.onload = () => { // 如果img不是来自缓存,那么只有在图片加载完成触发onload之后,才能拿到实际的witdh值 console.log(img.width); } img.src = 'http://xxx.com/user/helloWorld.jpg';// 如果存在缓存,在这里可以立即读取到图片的 witdh 值,否则会打印 0console.log(img.width);复制代码
至此一次XS-Leaks攻击就完成了。我们也可以请求一些带权限的链接来判断用户是否拥有某个网站的特权身份等等。虽然这个小栗子看起来危害不大,只是做到了当前用户和目标网站账号的关联,但是不要小看黑客们的脑洞,一个看起来不起眼的漏洞很可能会带来巨大损失。
这里给大家分享一个关于XS-Leaks的github地址,里边记录了与XS-Leaks相关的攻击方式、知识和实际案例,帮助大家更深刻地理解概念和攻击手段。
介绍了那么多,是时候总结一下XS-Leaks的一些防范措施了。可以看到XS-Leaks也是从第三方网站中发起攻击的,而且都是通过向目标网站发起请求而达到攻击目的,是不是和CSRF的攻击方式很相似?
没错,CSRF的防御手段同样可以让XS-Leaks对带鉴权的请求访问无效,从而降低危险。当然有些时候这种攻击其实并不需要鉴权就能达成目的,因此CSRF的防御手段并不能做到完美抵御,所以在浏览器层面增加缓存分区就显得非常有必要了:
XS-Leaks利用了浏览器缓存的漏洞实现了攻击,但是其实不仅仅浏览器可以缓存,web服务器也是可以有缓存的,服务器缓存是把请求过的资源暂且放在一个专门的缓存服务器(例如CDN)上,当下一个用户访问同样的资源时就可以直接从缓存服务器上拿到响应,从而减轻Web服务器的压力。
那假如我们可以把攻击代码通过某种方式放在CDN等缓存服务器中,那发起了相同资源请求的用户就都会拿到这份恶意代码从而遭受攻击,这不就实现了XS-Leaks的“存储型攻击”?
缓存服务器通过cache-key
来确定两个用户访问的是否是同一个资源,而这个cache-key
通常由请求方法、路径、query、host头组成。假如一个web服务器有如下请求响应:
该响应中将请求头X-Forwarded-Host
的值直接拼接到meta标签的content属性中,由此产生了XSS攻击的漏洞,因此我们通过发起一个如下请求:
GET /en?dontpoisoneveryone=1 HTTP/1.1Host: www.redhat.com X-Forwarded-Host: a."><script>alert(1)</script>复制代码
服务器就会返回如下响应,并缓存到缓存服务器中:
HTTP/1.1 200 OK Cache-Control: public, no-cache … <meta property="og:image" content="https://a."><script>alert(1)</script>"/>复制代码
由于X-Forwarded-Host
不属于cache-key
的一部分,因此当其他用户发起/en
请求时,服务器都会认为是请求同一个资源从而应用缓存策略,将上面的恶意响应直接响应给用户,造成攻击。
上述的例子来自于BlackHat2020议题之Web缓存投毒这篇文章,文章非常详细地介绍了Web服务器缓存攻击的原理和案例,有兴趣的小伙伴也可以看看。
尽管已经有这么多的措施来应对和抵御web的各种攻击,但依旧有一个又一个的安全漏洞被黑客们发现和攻破,强如Google,页面简洁如Google Search的网站,依然在2019年遭到了XSS攻击。而这次攻击却只需要一行代码:
<noscript><p title="</noscript><img src=x onerror=alert(1)>">复制代码
大致的原因是Google在转义插入文本时,使用了HTML提供的template
标签,使用template
是个很不错的选择,因为它可以用来解析HTML,并且在解析过程中不会触发JavaScript脚本执行。然而template
是JavaScript Disabled环境,而noscript在允许JavaScript和禁止JavaScript环境下的解析是不同的,这导致了这段恶意代码经过解析和sanitize之后仍然存在风险,最后成功实现了XSS攻击。有兴趣的小伙伴可以看看这篇关于这次XSS攻击的文章。
类似的“旁门左道”的攻击方式其实还很多,比如我们经常会用到的SVG
图片,SVG的结构和HTML结构非常相似,通过forginObject
标签我们还可以把script
或者其他HTML标签插入到SVG图片中,假如web服务器允许用户上传任意SVG图像,那么就会存在持久型XSS攻击的安全风险。HTML转义有时候也并非一两行代码就能搞定,当我们碰到类似文本编辑需要保留用户粘贴内容样式的时候,如何在保留这些html的同时规避XSS的风险就值得我们仔细斟酌设计。
众所周知,我们在开发网页的时候基本不会遇到HTML的语法报错问题,因为HTML的解析引擎有着非常强大的容错机制,以至于不管你怎么写,浏览器都能帮你把文档解析渲染出来。然而这种容错性和复杂的解析逻辑也使得XSS能够不断找到新的攻击方式。
事实上,当我在用某网站在线制作前面章节的两张交互图的时候,就“无意中”触发了我里边的img
代码,直接弹出了对话框,这也从侧面说明了web安全其实在很多开发团队中并没有得到足够多的重视,然而诸多的攻击案例告诉我们,我们的网站一旦遭受了攻击其损失可能是不可估量的,因此我们对于web安全,我们不能只是嘴上说说,纸上谈兵,或者只是当做面试的一个八股文知识点,而是应该时刻保持警惕,警钟长鸣才行。
受限于篇幅和精力,本次就先分享上述三个比较常见的web攻击手段,希望之后能够给大家分享更多关于web安全方面的知识和案例。
相关免费学习推荐:JavaScript(视频)
以上是javascript介紹前端安全知多少的詳細內容。更多資訊請關注PHP中文網其他相關文章!