XSS,三個讓資安人員頭痛的字母。
它的全名是 Cross-Site Scripting,中文叫做「跨站腳本攻擊」。
你可能會問:「Cross-Site Scripting 的縮寫不是應該是 CSS 嗎?」
沒錯,但因為 CSS 已經被「層疊樣式表」(Cascading Style Sheets)用掉了,所以改用 XSS 來代表。
這種攻擊從 1999 年就存在了,到現在已經超過 25 年。
聽起來是老掉牙的攻擊手法,但它依然是現今最具威脅性的攻擊之一。
根據 IBM X-Force 的雲端安全報告,XSS 是影響最大的攻擊類型第一名。
而知名的資安組織 OWASP(開放網路應用程式安全專案)也把它列為十大網頁應用程式漏洞的第三名。
為什麼一個存在這麼久的攻擊,到今天還是這麼猖獗?
這篇文章會帶你拆解 XSS 攻擊的原理,看一個實際的程式碼範例,最後介紹如何防範這種攻擊。
XSS 攻擊的運作原理
讓我們先看看 XSS 攻擊是怎麼運作的。
這個攻擊涉及三個角色:
攻擊情境:駭客如何發動 XSS 攻擊
第一步:駭客找到可以注入程式碼的地方
駭客會找到一個正常的、受信任的網站。
駭客會尋找網站上「可以輸入內容」的地方。
例如:留言板、評論區、搜尋框、個人簡介欄位等。
第二步:駭客注入惡意程式碼
假設這個網站有一個留言功能,使用者可以自由輸入文字。
正常使用者會輸入:「這個產品很棒!」
但駭客會輸入一段惡意的程式碼。
如果網站沒有做好防護,這段程式碼就會被存進資料庫,並顯示在網頁上。
這時候,惡意程式碼就像一顆地雷,埋在網頁裡面,等待下一個訪客觸發。
第三步:受害者瀏覽網頁,惡意程式碼被執行
當其他使用者瀏覽這個頁面時,瀏覽器會載入頁面上的所有內容。
包括駭客注入的那段惡意程式碼。
你可能會問:「我只是瀏覽網頁而已,為什麼會執行程式碼?」
這是因為瀏覽器的運作方式。
當你打開一個網頁時,瀏覽器會自動讀取並執行網頁裡的所有程式碼。
這就像你打開一本有聲書,音樂會自動播放一樣。
你不需要按任何按鈕,瀏覽器就會幫你執行網頁裡的 JavaScript。
所以只要網頁裡藏有惡意程式碼,你一打開頁面就中招了。
這時候,網站就在不知不覺中成為駭客的幫兇。
網站本身是無辜的,但它變成了「惡意程式碼的傳播管道」。
關鍵在於:這段惡意程式碼是在「受信任的網站」的環境下執行的。
瀏覽器不知道這段程式碼是駭客注入的,它只知道「這是這個網站的程式碼,我應該執行它」。
這就是 XSS 攻擊的可怕之處。
為什麼叫做 XSS(Cross-Site Scripting)?
了解了攻擊原理後,我們來看看這個名字的意思。
Scripting(腳本)
「腳本」指的是一段可以被執行的程式碼。
在網頁的世界裡,最常見的腳本就是 JavaScript。
當瀏覽器看到網頁裡有 JavaScript 程式碼時,就會自動執行它。
駭客注入的惡意程式碼,就是一段「惡意的腳本」。
Cross-Site(跨站)
「跨站」是什麼意思呢?
先來看看「同站」的情況。
正常情況下,網頁裡的程式碼都是網站自己寫的。
例如,example.com 網站裡的程式碼,是 example.com 的開發者寫的。
這種情況叫做「同站」——程式碼來自同一個網站。
但 XSS 攻擊不一樣。
駭客把自己的程式碼「偷渡」到別人的網站裡。
例如,駭客把惡意程式碼塞進 example.com 的留言板。
這段程式碼不是 example.com 寫的,而是駭客從「外部」注入的。
這種「外部程式碼混入網站」的情況,就是「跨站」。
組合起來:Cross-Site Scripting
- Cross-Site(跨站):惡意程式碼是從外部注入的,不是網站自己的
- Scripting(腳本):注入的是可執行的程式碼
合在一起就是:駭客把惡意腳本注入到別人的網站,讓訪客的瀏覽器執行。
這就是 XSS(Cross-Site Scripting)的意思。
XSS 攻擊可以造成什麼危害?
一旦惡意程式碼在受害者的瀏覽器中執行,駭客可以做很多事情。
篡改網頁內容,欺騙使用者
駭客可以用 JavaScript 修改網頁上顯示的內容。
想像一下這個情境:
你登入了網路銀行,想要轉帳 10,000 元給朋友「王小明」。
你填好表單,確認畫面顯示「轉帳金額:10,000 元,收款人:王小明」。
看起來沒問題,你按下確認。
但實際上,駭客的程式碼早就在背後偷偷修改了表單的資料。
你「看到」的是轉給王小明,但「實際送出」的是轉給駭客的帳戶。
等你發現帳戶少了錢,錢早就被轉走了。
這就是篡改網頁的可怕之處:你眼睛看到的,不一定是真的。
竊取 Cookie,劫持使用者的登入狀態
還記得 Cookie 嗎?
當你登入一個網站時,伺服器會產生一個「Session ID」(會話識別碼),代表你的登入身份。
這個 Session ID 會被存放在瀏覽器的 Cookie 裡面。
之後每次你發送請求,瀏覽器都會自動帶上這個 Cookie,伺服器就知道「這是剛才登入的那個人」。
換句話說,誰拿到你的 Cookie,誰就能假冒你的身份。
那駭客要怎麼拿到你的 Cookie 呢?
這就要說到 JavaScript 和瀏覽器的關係了。
當 JavaScript 在網頁上執行時,它可以存取瀏覽器提供的各種功能。
其中一個功能就是「讀取 Cookie」。
JavaScript 只要執行 document.cookie,就能讀取到當前網站的所有 Cookie。
這是瀏覽器設計的正常功能,讓網站可以管理使用者的登入狀態。
但問題是:駭客注入的惡意程式碼也是 JavaScript。
它在「受信任的網站」環境下執行,自然也有權限讀取該網站的 Cookie。
於是駭客可以透過 JavaScript 讀取你的 Cookie,然後傳送到駭客自己的伺服器。
駭客拿到你的 Cookie 後,就可以「劫持」(Hijack)你的登入狀態。
如果這是你的銀行網站,駭客就可以用你的身份登入,把你的錢轉走。
如果這是電商網站,駭客就可以用你的身份購物,讓你付錢。
植入惡意軟體
駭客可以透過 JavaScript 觸發檔案下載。
想像一下這個情境:
你只是瀏覽一個購物網站的商品頁面。
突然間,瀏覽器跳出一個下載視窗,顯示「系統更新程式.exe」。
如果你不小心點了「執行」,惡意軟體就會安裝到你的電腦裡。
更狡猾的做法是,駭客會讓下載視窗看起來像是正常的系統通知:
「您的 Flash Player 已過期,請點擊更新」
很多人看到這種訊息就會直接點下去,結果電腦就被植入了木馬程式。
一旦惡意軟體進入你的電腦,駭客就可以:
- 監控你的鍵盤輸入,竊取所有密碼
- 存取你電腦裡的檔案
- 把你的電腦變成殭屍網路的一部分,用來攻擊其他人
導向釣魚網站
駭客可以透過 JavaScript 把你重新導向到另一個網站。
想像一下這個情境:
你在瀏覽某個論壇,點了一篇看起來很正常的文章。
頁面閃了一下,你發現自己還是在同一個論壇,但網頁顯示「您的登入已逾時,請重新登入」。
你沒多想,就輸入了帳號密碼。
但其實,你已經被導向到駭客做的假網站了。
這個假網站長得跟真的一模一樣,網址也很相似(例如把 forum.com 改成 forurn.com,把 m 換成 rn)。
你輸入的帳號密碼,全部都被駭客收走了。
這就是「釣魚攻擊」(Phishing)。
而 XSS 讓駭客可以在「你信任的網站」上發動釣魚攻擊,讓你更難察覺異常。
另一種攻擊方式:透過惡意連結
上面介紹的是「把程式碼存進網站,等人來看」的攻擊方式。
但駭客還有另一種手法:直接發送一個「帶有惡意程式碼的連結」給你。
這個連結可能透過 Email、簡訊、或社群媒體傳送。
連結看起來像是正常的網站網址,但裡面藏著惡意程式碼。
怎麼藏的呢?
你可能看過這種網址:https://example.com/search?q=手機
問號後面的 q=手機 是搜尋的關鍵字。
當你點擊這個連結,網站會顯示「您搜尋的關鍵字是:手機」。
駭客的手法是:把「手機」換成一段惡意程式碼。
當你點擊這個連結時,會發生什麼事?
首先,瀏覽器會向網站的伺服器發送請求,請求裡包含完整的網址。
伺服器收到請求後,會從網址裡取出 q= 後面的內容(也就是搜尋關鍵字)。
接著,伺服器會產生一個網頁,把這個關鍵字放進 HTML 裡面,變成「您搜尋的關鍵字是:OOO」。
最後,伺服器把這個網頁傳回給瀏覽器。
問題就出在這裡。
如果伺服器沒有檢查 q= 後面的內容是什麼,就直接放進 HTML 裡,那麼惡意程式碼也會被放進去。
所以惡意程式碼就從「網址」跑到「網頁」上了。
瀏覽器收到這個網頁後,看到裡面有程式碼,就會自動執行。
瀏覽器不知道這段程式碼是從網址帶進來的,它只知道「網頁裡有程式碼,我要執行它」。
因為程式碼是從「網址」帶進來,再被「反射」到網頁上的,所以叫做「反射型」XSS。
這兩種方式的差別在於:
不管是哪種方式,結果都一樣:駭客的程式碼在你的瀏覽器裡執行了。
XSS 攻擊程式碼範例
讓我們看一個簡單的範例。
這個範例不會造成實際傷害,但你可以理解攻擊的原理。
假設有一個網站的搜尋功能,網址長這樣:
https://example.com/search?q=手機當你搜尋「手機」時,網頁會顯示:「您搜尋的關鍵字是:手機」。
如果網站沒有做好防護,駭客可以構造這樣的網址:
https://example.com/search?q=<script>alert('XSS')</script>讓我們拆解這個網址:
當使用者點擊這個連結時,網頁會顯示:
您搜尋的關鍵字是:<script>alert('XSS')</script>瀏覽器看到 <script> 標籤,就會執行裡面的程式碼。
結果是畫面會跳出一個警告視窗,顯示「XSS」。
這個範例只是跳出一個無害的視窗。
但駭客可以把 alert('XSS') 換成任何惡意程式碼,例如竊取 Cookie 的程式碼。
開發者如何防範 XSS 攻擊?
在講防護方式之前,我們先來理解 XSS 攻擊的核心問題。
瀏覽器在載入網頁時,會區分兩種東西:
- 資料:純粹的文字內容,只是要「顯示」給使用者看的
- 程式碼:需要「執行」的指令,例如 JavaScript
正常情況下,使用者在留言板輸入的內容應該是「資料」。
例如,使用者輸入「這個產品很棒!」,網頁應該只是把這段文字顯示出來。
但 XSS 攻擊的問題在於:
駭客輸入的內容「看起來像程式碼」,瀏覽器就會把它當成程式碼來執行。
例如,駭客輸入 <script>...</script>,瀏覽器看到 <script> 標籤,就會認為「這是程式碼,我要執行它」。
所以 XSS 攻擊的核心是:駭客把程式碼偽裝成資料,讓瀏覽器誤以為是要執行的指令。
理解了這個核心問題,防護的思路就很清楚了:
確保使用者輸入的內容只會被當成「資料」,而不是「程式碼」。
具體來說,有兩種做法。
防護方式一:不信任使用者輸入,驗證並過濾
第一個原則:永遠不要信任使用者輸入的任何內容。
不管是留言、評論、搜尋關鍵字,還是任何使用者可以輸入的地方,都要進行驗證。
做法是:檢查並拒絕可疑的內容。
如果你的留言功能只應該接受純文字,那就檢查輸入的內容:
- 純文字 → 允許
- 包含
<script>標籤 → 拒絕 - 包含
onclick、onerror等事件屬性 → 拒絕 - 包含 SQL 指令(順便防止 SQL Injection) → 拒絕
// 簡單的驗證範例
function validateInput(input) {
// 檢查是否包含可疑的標籤或屬性
const dangerousPatterns = /<script|onclick|onerror|javascript:/i;
if (dangerousPatterns.test(input)) {
return false; // 拒絕這個輸入
}
return true; // 允許這個輸入
}防護方式二:輸出時進行編碼
第二個重要的防護是:在輸出內容到網頁時,進行編碼(Encoding)。
什麼是編碼?
就是把有特殊意義的字元,轉換成「純文字」的對應版本。
舉個例子。
如果使用者輸入了 <script>alert('XSS')</script>,經過編碼後會變成:
&lt;script&gt;alert('XSS')&lt;/script&gt;當瀏覽器看到 < 時,它知道這只是要「顯示」一個小於符號,而不是一個 HTML 標籤的開始。
所以畫面上會顯示:<script>alert('XSS')</script>
但這段文字只是被「顯示」出來,不會被「執行」。
這樣就成功阻止了 XSS 攻擊。
防護重點整理
驗證輸入 + 編碼輸出,兩者都要做,才能提供完整的防護。
更多資源:OWASP Cheat Sheet
如果你想了解更多 XSS 防護的細節,推薦參考 OWASP 提供的 Cheat Sheet。
OWASP 是知名的資安組織,他們整理了詳細的程式碼範例和防護建議。
搜尋「OWASP XSS Prevention Cheat Sheet」就可以找到。
小結
讓我們回顧一下這篇文章的重點:
- XSS 是什麼:Cross-Site Scripting,跨站腳本攻擊,駭客把惡意程式碼注入到網頁中
- 攻擊原理:駭客注入的程式碼會在受害者的瀏覽器中執行,而且是在「受信任網站」的環境下執行
- 可能的危害:竊取 Cookie 劫持登入狀態、篡改網頁內容、植入惡意軟體、導向釣魚網站
- 防護方式一:驗證並過濾使用者輸入,拒絕可疑的內容
- 防護方式二:輸出時進行編碼,把特殊字元轉換成純文字
XSS 攻擊已經存在超過 25 年,但至今仍是最常見的攻擊之一。
身為開發者,做好輸入驗證和輸出編碼,就能大幅降低被攻擊的風險。