低成本打造一個頻寬無限的網站 —— No.5 免費空間的挖掘
突破限制
由於 SW 非常強大,因此使用條件也是非常嚴格的,以免被惡意使用。
例如 SW 必須在 HTTPS 站點上使用。這本是件好事,徹底杜絕了中間人的隱患,但現在卻成了一道門檻。
同時,這還引發了另一個問題:由於 HTTPS 站點是禁止讀取 HTTP 資料的,因此我們的節點也必須是 HTTPS 站點!
此外 SW 也得遵守同源策略。如果要讀取第三方站點的資料,目標響應頭裡還得有 Access-Control-Allow-Origin: *
欄位 —— 這對於免費空間來說,是個不小的要求。
免費空間,要同時滿足上述兩個條件,確實有些苛刻。好在 SW 能和頁面互動,因此在代理網頁資源時,可以往其中插入一個輔助指令碼,這樣就能把任務交給頁面來實現。畢竟頁面裡有豐富的 DOM 功能,可玩出各種奇技淫巧。
頁面代理
對於 不支援 ACAO、但支援 HTTPS 的站點,可通過頁面代理實現 CORS。
我們通過 iframe 引入目標站點下的一個代理頁面,由它來讀取資料,然後將結果 postMessage
給父頁面:
若是追求更高效能的話,還可通過 MessageChannel
直接從 iframe 傳到 SW 裡,減少一次訊息中轉:
只要目標站點能部署 html 資源,就可以用這個方案,讀取站點下任意型別的資源!
需注意的是,該方案依賴頁面。假如使用者關閉了所有頁面,然後通過位址列訪問資源 —— 這時 SW 雖能攔截請求,但由於沒有可互動的頁面,因此無法使用該方案。
不過,有個簡單的辦法可以解決這個問題:我們讓 SW 先返回一個臨時的過渡頁面,用它來配合內容載入;完成後頁面自動重新整理,這時 SW 就能給出真正的內容了!
JSONP
對於上述情況,還有種不依賴頁面的辦法 —— 我們將資源打包在指令碼檔案裡,通過 JSONP 的方式直接在 SW 中載入。
當然這種方案缺陷十分明顯:指令碼是文字格式的,編碼二進位制資源會增加不少體積。
此外,Worker 中載入指令碼的函式 importScripts
是同步阻塞的,因此會對程式帶來很大影響。除非使用 Sub Worker(在 Worker 中巢狀 Worker),但目前很多瀏覽器包括 Chrome 都不支援,所以暫不考慮。
混合內容
對於 不支援 HTTPS、但支援 ACAO 的站點,這時就需要利用 混合內容(Mixed Content
)機制了。
雖然瀏覽器原則上不允許 HTTPS 頁面引用 HTTP 資源,但對於風險較低的資源,例如圖片、多媒體,仍然是允許的!
因此,我們可將原始資料作為畫素,打包在圖片裡。頁面通過設有 crossOrigin
屬性的 Image
載入圖片,然後繪製到 canvas 上,這樣就能讀取畫素,從而得到原始資料了!
演示:https://www.etherdream.com/FunnyScript/jszip/decode.html
關於資料編碼成圖片的細節,可參考《利用 canvas 實現資料壓縮》。不過和文中不同的是,如今我們通過本地工具編碼圖片,因此最終結果還可以用 PNGout、zopflipng 等工具進行強力優化。
當然,資料打包成圖片後,體積不可避免會有所增加。但反正頻寬是免費的,有總比沒有好:)
不過,踩混合內容的黃線,也是有一定代價的。例如 Chrome 瀏覽器,介面上的證書圖示不再是綠色了,並且控制檯裡也會出現告警:
對於這個問題,倒是有個簡單的緩解策略:假如當前開著多個頁面(Tab)的話,我們可以讓 SW 選一個不可見的,由它來載入資源 —— 這樣即使介面有變化,使用者也不會立即看見了:)
Flash 代理
對於既不支援 ACAO 又不支援 HTTPS 的破站點,只能用同樣破舊的東西來配它 —— Flash。
儘管瀏覽器並不允許 HTTPS 頁面載入 HTTP Flash,但我們可以 先載入一個 HTTPS Flash 作為跳板,然後通過它來載入 HTTP 的資源。
因為外掛內部是不受瀏覽器管控的,所以就能利用 Flash 寬鬆的限制,繞過混合內容策略!
只要目標站點支援 xml 資源(用於存放 cross domain xml),我們就能讀取該站點下任意型別的資源!
退一步,即使目標站點不支援 xml 也沒關係,能支援 swf 檔案也可以。我們用這個 swf 作為目標站點的代理,這樣就解決「網路通訊」的同源策略了。
同時,再通過 AS 指令碼開放自身許可權:
Security.allowDomain('*');
這樣,就能解決「模組互動」的同源策略了。
這裡用了兩個 swf 做代理 —— 前者規避混合內容,後者規避同源策略,是不是很巧妙:)
有了這個辦法,那些能上傳 swf 的論壇,我們就能讀取和它同站點的圖片附件了!
不過比較尷尬的是,如今主流瀏覽器都已禁用 Flash,當初寫的這些「奇技淫巧」也沒什麼卵用了。。。
總結
HTTPS | ACAO | 獲取方式 | 資訊載荷型別 | 依賴頁面 | 主要缺陷 |
---|---|---|---|---|---|
√ | √ | fetch() | * | × | - |
√ | × | 頁面代理 | * (html) | √ | 額外嵌入一個頁面 |
√ | × | JSONP | js | × | Worker 中會阻塞 |
× | √ | 圖片畫素 | image | √ | 混合內容介面警告 |
× | × | Flash 代理 | * (xml 或 swf) | √ | 很多瀏覽器已禁用 |
這裡我們只是從 HTTPS 和 ACAO 兩個條件進行探討。現實中,當然還有更復雜的情況。
例如,一些圖床同時支援 HTTPS 和 ACAO,但只能上傳圖片格式。對於這種情況,其實不依賴頁面也是可以載入的 —— 我們可以直接在 SW 中 fetch 圖片,然後用 JS 版的影象解碼庫,還原出畫素裡的資料。
更進一步,我們還可以檢測圖床是否會修改上傳的原始檔案。如果不修改的話,我們可以把資料藏在圖片輔助資訊裡,甚至直接附加在檔案末尾,這樣直接擷取即可,連解碼都不需要了!
總之,只要發揮想象,很多網站都可以利用起來,在我們寬頻緊張的情況下,充當免費的後備節點:)