文件上傳對話框是一直以來就存在的網頁控件。javascript
到了 HTML5 時代,增長了更多的功能,例如支持文件多選。Chrome 甚至還支持「上傳文件夾」這一私有特徵:html
<input type="file" webkitdirectory />
在給用戶方便的同時,其安全隱患也逐漸出現。用戶平時在下載時,理所固然的彈出的是保存對話框,所以經常不仔細看就作出了選擇。前端
這極有可能被攻擊者所利用。一些惡意網站在用戶點擊下載時,故意彈出一個上傳對話框。只要用戶一疏忽,就把選中的文件夾給上傳了!java
下載對話框web
上傳對話框算法
固然,僅僅依靠默認的上傳,這種攻擊方式仍有較大難度。由於整個文件夾可能很是大,上傳須要好久的時間。canvas
若是用戶等了半天也沒看見下載進度,或許就會刷新重試,甚至放棄了。瀏覽器
然而,HTML5 帶來了一個新的規範 —— File API,容許腳本訪問文件。安全
但因爲沙箱限制,腳本沒法訪問任何一個本地文件,除非用戶主動受權。如何受權?最多見的,就是「上傳對話框」了。xss
事實上,現在的上傳對話框,早已不是從前「選擇哪一個文件」的功能,而是「容許腳本訪問哪一個文件」的權限申請!只不過界面上沒有提示罷了。
例如,一個傳統的文件上傳控件。當用戶選中文件後,便可經過 File API 讀取文件內容:
<input id="dialog" type="file" /> <script> dialog.onchange = function(e) { var reader = new FileReader(); reader.onload = function() { console.log(this.result); }; reader.readAsText(this.files[0]); }; </script>
或許你已注意到,File 位於files[]
而不是file
,這正是給文件夾預留的!
在 Chrome 裏,上傳控件只要加上 webkitdirectory
屬性,就變成文件夾選擇框。這時一旦用戶選中某個文件夾,瞬間就賜予腳本訪問整個文件夾的權限!
<input id="dialog" type="file" webkitdirectory /> <script> dialog.onchange = function(e) { var files = this.files; var table = {}; for (var i = 0; i < files.length; i++) { var f = files[i]; var dt = new Date(f.lastModified); table[i] = { path: f.webkitRelativePath, size: f.size, modified: dt.toLocaleString() }; } console.table(table); }; </script>
因而,用戶本想將文件保存在桌面上,結果卻將桌面上的全部資料,被攻擊者的腳本拿到!
一旦腳本可主動訪問,咱們能夠用更靈活的方式處理這些文件,無需再用傳統落後的方式上傳。咱們能夠直接在前端分析出「有價值」的文件,例如:
備註文件、腳本、批處理、電子表格等,極可能存有一些敏感信息,並且體積小价值大,優先將其上傳;
圖片則可經過 canvas 縮放,先傳較小的縮略圖。當接收端發現有意義時,再傳輸原文件。
對於一些體積較大但意義不大的文件,則能夠直接忽略。
因爲 HTTP 上傳是沒有壓縮的,所以在傳輸文本文件時效率很低。咱們能夠藉助 Flash 內置的LZMA
壓縮算法,極大提高傳輸效率。若是不支持 Flash,也可使用asm.js
版的 LZMA 壓縮器,配合Worker
線程在後臺壓縮和傳輸。
同時,將多個小文件合併後再壓縮,可進一步提升壓縮率。再多開幾個鏈接,上傳速度便可大幅提高。
不過即便再優化,仍有傳不完的可能。所以,咱們得將沒傳完的內容儲存起來,當用戶再次回來時,繼續傳輸。
得益於 HTML5 的 Storage API,這不難實現。
事實上,當用戶受權了某個文件夾時,咱們首先要作的不是發送,而是讀出文件夾內容,當即備份到 Storage 裏。畢竟,文件的讀取須要用戶主動配合,是很是珍貴的;而訪問 Storage 則無需任何條件。
當備份完成後,再從 Storage 裏讀取、發送、刪除。這樣,即便中途頁面刷新或關閉了,下次回來時,仍能從 Storage 中繼續。
考慮到每一個域的 Storage 容量有限,咱們可使用iframe
嵌入多個不一樣域的頁面,而後經過postMessage
進行數據的分發和彙總,這樣就不受容量限制了。
固然,能不能無限容量還得看瀏覽器策略,否則硬盤會被撐滿
將數據存放在 Storage 裏還有另外一個好處,即便用戶永不回來,但數據仍持久保存着。只要之後一旦進入其餘的站點,只要是咱們可控的,仍有機會繼續上傳。(例如將用戶引到咱們佈置了 XSS 的站點上)
在以前《延長 XSS 生命期》 中介紹過,可使用各類黑魔法來提高腳本有效期。
利用這個原理,即便當前頁面關閉,其餘關聯的頁面也能繼續上傳。事實上,除了文中提到的方法,現在還有一個新的 API —— SharedWorker,它可讓 Worker 共享於多個頁面,只要有一個存在,線程就不會中止。可讓續點時丟失的數據更少。
因爲上傳控件有着獨特的界面,如何才能讓用戶主動去點呢?萬能的方法是點擊劫持(Clickjacking)。
不過本場景無需這麼麻煩,只需簡單的調用控件的click
方法就能夠了。
<a href="ed2k://|file|xxxxxxxxxx.avi" id="download">高速下載</a> <script> var uploader = document.createElement('input'); uploader.type = 'file'; uploader.webkitdirectory = true; download.onclick = function(e) { uploader.click(); // 彈出受權對話框 e.preventDefault(); // 屏蔽下載對話框 }; </script>
咱們將屏蔽超連接的默認行爲,將其傳遞給上傳控件,便可召喚出「文件夾受權」對話框了!
同時,爲了避免讓眼亮的人發現對話框上的破綻,咱們使用一些第三方的下載方式,例如電驢、迅雷等等,讓人們誤覺得就是這樣的。
固然,這只是一個小例子。只要頁面作的真實,下載內容引人入勝,用戶天然就會中招。
本覺得 webkitdirectory
的這個私有屬性很快就會放棄,至少是更強的安全提示。不過至少如今也沒更新,所以上網時仍是要多加留心,看清楚了再作決定。
固然,這篇只是最先的「交互欺騙」探索。事實上,深刻挖掘會發現可利用點遠不只此。
現在的瀏覽器在視覺、音頻上的體驗已經很是完善,攻擊者甚至能夠在網頁裏,高度模擬一個本地應用的交互效果。讓用戶誤覺得是瀏覽器以外的程序彈出的界面,從而進行釣魚。
參考:http://www.cnblogs.com/index-html/p/dialog-phishing.html