本文簡要介紹了 Script Error 問題的來龍去脈,但也不局限于 Script Error,對于通用的系統(tǒng)性問題,應(yīng)該找到系統(tǒng)性解決方案,進(jìn)而治標(biāo)治本。
Script Error 原因與當(dāng)前解法
受瀏覽器同源策略限制,未知跨域腳本執(zhí)行錯誤時,拋出的錯誤信息為 "Script error.",導(dǎo)致開發(fā)者無法定位具體錯誤。為了獲取詳細(xì)錯誤信息及堆棧,一般解法是給 Script 標(biāo)簽配置 crossorigin 屬性,同時對應(yīng)腳本服務(wù)端需配置Access-Control-Allow-Origin 響應(yīng)頭。
另外還有一些 hack 解法,對瀏覽器原生 API 做代理,將業(yè)務(wù)代碼放在 Try Catch 作用域中執(zhí)行,但寫好代理方法是不容易的,粗制濫造的代理方法會制造很多隱藏 Bug,并且大量 Try Catch 在一些 JS Engine 中也存在額外性能損耗,為了解決 Script Error 采用此方案得不償失。
還有什么問題
-
crossorigin不好加
異步加載腳本套娃,A 加載 B,B 加載 C,以至于不知道加載了哪些外部腳本
需要服務(wù)端配合設(shè)置響應(yīng)頭 Access-Control-Allow-Origin
-
crossorigin加不了
外部注入代碼,如瀏覽器插件、定制 Webview 容器(xx 瀏覽器)
-
無效 Script Error 數(shù)據(jù),難以評估對業(yè)務(wù)實際影響,并且耗費監(jiān)控資源
溯源:為什么是 Script Error
從 2006 年一篇安全漏洞文章說起:I know if you're logged-in, anywhere
在那個年代大量網(wǎng)站都是服務(wù)端渲染,服務(wù)端根據(jù)用戶登錄態(tài)返回不同頁面內(nèi)容,黑客通過 Script 加載目標(biāo)站點,用戶已登錄、未登錄返回的 Response 內(nèi)容不同,報錯信息也會有差異,這樣就可以通過報錯信息區(qū)分用戶是否登錄,進(jìn)一步展開針對性的攻擊。
已登錄:
未登錄:
對于其他站點也是類似,錯誤信息中總會有差異,比如亞馬遜登錄和未登錄,報錯的 LineNo 不同。
基于此,WHATWG 對錯誤信息透出制定了規(guī)范:
Chrome 實現(xiàn):
《I know if you're logged-in, anywhere》地址:https://blog.jeremiahgrossman.com/2006/12/i-know-if-youre-logged-in-anywhere.html
Script Error 規(guī)范是否能調(diào)整
通過以上信息,我們可以理解 Script Error 的設(shè)計初衷以及其合理性,但我也有疑問,在今天瀏覽器同源策略比較完善的情況下,是否有必要屏蔽所有信息(error message、lineno、colno、url)?能否將發(fā)生 Script Error 的腳本 url 暴露出來,以便開發(fā)者收集到錯誤信息時快速定位錯誤來源,這樣也方便評估影響面,比如明顯是注入的腳本錯誤,直接忽略即可。
翻閱 WHATWG Github 歷史 issue,發(fā)現(xiàn)已經(jīng)有過相關(guān)討論,很明確答案是 No,大概原因是當(dāng)前的同源策略已經(jīng)很全面(復(fù)雜),不想在挖坑。以至于對 unhanlderejection,連 Script Error 都不愿意報。
相關(guān)討論地址:https://github.com/whatwg/html/issues/2440 unhanlderejection地址:https://github.com/whatwg/html/issues/5051
其他大廠如何處理 Script Error
我在幾個大廠網(wǎng)站上做了測試,加載一個第三方腳本,第三方腳本一定會報錯,看看對應(yīng)站點如何處理。
var s = document.createElement('script');
s.src = 'https://g.alicdn.com/dinamic/h5-tb-cart/5.0.41/index.min.js';
document.body.AppendChild(s);
-
google:常規(guī)處理,直接上報 Script Error
-
Twitter: 通過 CSP 策略攔截了未知腳本加載,包括 Github、FaceBook 都采用類似方案
-
QQ 視頻:除了上報 Script Error,并監(jiān)控上報異步加載的腳本
面向未來我們應(yīng)該如何處理 Script Error
面向未來看問題,我們不能與標(biāo)準(zhǔn)背道而馳,同源策略是當(dāng)前解決 Web 安全問題的重要手段,在未來只會更完善,我們應(yīng)該積極了解與應(yīng)用。當(dāng)前國內(nèi)互聯(lián)網(wǎng)對同源策略的了解與應(yīng)用大多止步于Access-Control-Allow-Origin:*,這是遠(yuǎn)遠(yuǎn)不夠的。
因此,面向未來 Script Error 問題 Twitter 的處理方式相對合理,只允許站點加載白名單腳本,對白名單腳本逐個做 CrossOring 等配置,同時也杜絕了外部腳本注入。對于淘寶來說,受限于業(yè)務(wù)體量以及歷史包袱,做這種改造難度可想而知,但我們應(yīng)該朝這個方向努力,而不是讓開發(fā)者面對 Script Error 手足無措,靠猜測或是加錯誤過濾解決問題。
回到當(dāng)下,短期的解決方案要增強(qiáng)跨域腳本的感知能力,可以配置 CSP Report Only 上報跨域腳本,也可以通過原始手段統(tǒng)計,進(jìn)而對相關(guān)腳本做跨域配置,對于明顯的跨域腳本如埋點、喚端、以及安全系列腳本,缺少 crossorigin 的盡快修復(fù)。
CSP Report Only地址:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
document.querySelectorAll('script[src]:not([crossorigin])')
本文簡要介紹了 Script Error 問題的來龍去脈,但也不局限于 Script Error,對于通用的系統(tǒng)性問題,應(yīng)該找到系統(tǒng)性解決方案,進(jìn)而治標(biāo)治本。
參考文檔
-
what is script error(地址:https://blog.sentry.io/2016/05/17/what-is-script-error)
-
Cryptic "Script Error." reported in JAVAscript in Chrome and Firefox(地址:https://stackoverflow.com/questions/5913978/cryptic-script-error-reported-in-JavaScript-in-chrome-and-firefox)
-
解決 "Script Error" 的另類思路(地址:https://juejin.cn/post/6844903727820718094#heading-6)
-
IOS Privacy: Instagram and Facebook can track anything you do on any website in their in-app browser(地址:https://krausefx.com/blog/ios-privacy-instagram-and-facebook-can-track-anything-you-do-on-any-website-in-their-in-app-browser)
-
I know if you're logged-in, anywhere(地址:https://blog.jeremiahgrossman.com/2006/12/i-know-if-youre-logged-in-anywhere.html)
-
HTML Spec: Runtime script errors(地址:https://html.spec.whatwg.org/multipage/webappapis.html#runtime-script-errors)
-
"Script error." message in window.onerror makes bad DevExp trade off(地址:https://github.com/whatwg/html/issues/2440)
-
unhandledrejection should fire even for muted scripts(地址:https://github.com/whatwg/html/issues/5051)
-
Content-Security-Policy-Report-Only(地址:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only)