新視野行銷企劃

sandbox 屬性與跨來源安全性最佳實踐

「sandbox 屬性與跨來源安全性最佳實踐」主題封面圖,採扁平化設計風格,顯示網頁瀏覽器介面、盾牌與鎖頭符號,象徵 iframe sandbox 限制與網頁跨來源安全防護,背景為藍色科技感配色,呈現現代化的前端安全概念。
現代網站經常需要嵌入第三方內容(如影片播放器、地圖、社群媒體小工具等),但直接使用 iframe 載入外部內容可能帶來嚴重的安全隱患。HTML5 提供的 sandbox 屬性能將內嵌內容約束在「沙箱」環境中,限制其可能的惡意行為,同時讓我們按需開啟必要功能。本文將深入介紹 sandbox 屬性的機制、各種控制標誌(flags)的作用,討論未使用 sandbox 的風險、以及搭配 Content Security Policy(CSP)使用的考量,並提供範例程式碼與常見問答,協助中級前端開發者瞭解跨來源內容的安全最佳實踐。

sandbox 基本概念與作用機制

sandbox 是 iframe 元素的一個屬性,啟用後會對該框架中的內容套用一系列預設的安全限制。換句話說,sandbox 會將內嵌頁面放入一個隔離的沙箱環境中,讓它即使載入了不受信任的第三方內容,也只能執行有限的操作。當我們在 <iframe> 標籤中加入 sandbox 屬性而不帶任何額外值時,瀏覽器將對該框架內容施加以下限制:

  • 腳本禁止執行:沙箱中的頁面無法執行任何 JavaScript 腳本(除非另行允許,下節詳述)。這防止了未經授權的腳本攻擊,如惡意的跨站指令碼。
  • 阻擋網路請求:預設情況下,沙箱頁面無法發送 AJAX/XHR 等網路請求。這表示即使有腳本(已被禁止),也無法偷偷向外部伺服器傳輸資料。
  • 禁用本地儲存:沙箱內容不能讀寫瀏覽器中的任何本地儲存資料,例如 localStorage 或 document.cookie。這避免了內嵌內容存取或竊取主網站的認證資訊或會話資訊。
  • 禁止彈出視窗:沙箱內容不能開啟新的瀏覽器視窗或對話框(如使用 window.open、target="_blank" 等)。這可防止惡意內容透過不斷跳出廣告或釣魚視窗干擾使用者。
  • 禁止表單提交:沙箱中的表單無法提交資料。換言之,任何 <form> 元素的送出動作都被攔截,避免內嵌的惡意表單將使用者資料送往第三方。
  • 停用外部外掛:沙箱內無法載入任何外部外掛程式(例如 Flash 插件)。由於外掛通常執行原生程式碼,風險更高,因此在沙箱中一律被禁止。

上述最低權限的沙箱模式極為嚴格,確保了內嵌內容幾乎不可能對外部造成危害。然而完全鎖死所有功能也降低了實用性——大多數情況下我們仍需要按需開啟特定能力來讓嵌入內容正常工作。例如,有些小工具可能需要執行腳本或提交表單。為此,sandbox 屬性允許我們附加一系列「權限標誌」(flags)來逐項解除某些限制。下一節將逐一介紹這些標誌及其使用建議。

sandbox 標誌(flags)逐一解說與使用建議

當我們在 iframe 的 sandbox 屬性中加入特定的關鍵字時,即可解除對應的沙箱限制。這些關鍵字彼此以空白分隔,可以同時指定多個。例如 <iframe src="URL" sandbox="allow-scripts allow-same-origin"> 就是讓沙箱允許腳本執行和保留同源權限。以下是常用的 sandbox 標誌列表及其作用:

  • allow-forms – 允許沙箱內的表單送出資料。預設情況下表單提交會被沙箱阻擋,使用此標誌即可開放表單提交功能。
    使用建議:僅在內嵌內容需要提交資料(例如登入表單)時加入,否則保持禁用以策安全。
  • allow-scripts – 允許在沙箱內執行 JavaScript 腳本。預設不含該標誌時,iframe 中的腳本都不會運作。
    使用建議:儘量避免開啟腳本,除非嵌入的第三方內容確實需要 JS 支援。如果必須開啟,也應同時考量其他標誌的搭配(詳見下文的安全注意事項)。
  • allow-same-origin – 允許沙箱內容被當作與父頁面「同源」處理。預設沙箱會將內嵌文件的來源視作一個獨立且不相容的來源(其 Origin 會被強制設為 null),導致同源政策檢查一律失敗。加上此標誌後,iframe 內容就保留原本的來源,比如載入自 https://example.com 的頁面仍被視為來自該網域。
    使用建議:除非需要讓父子頁面或沙箱內容與其原始網域互動(如共享 cookies、使用相同的 localStorage 或進行資料傳輸),否則應避免使用 allow-same-origin。開啟此權限會增加來源資料外洩的風險,應謹慎評估。
  • allow-popups – 允許沙箱內容開啟彈出視窗或新分頁(例如使用 window.open,或連結設定 target="_blank")。未設置此標誌時,沙箱中嘗試彈出新視窗的操作將會被瀏覽器靜默阻擋。
    使用建議:如非必要(例如嵌入的內容需要開新視窗顯示更多資訊),儘量不要開啟此權限,以免第三方內容惡意地開啟釣魚網站或廣告頁面。
  • allow-top-navigation / allow-top-navigation-by-user-activation – 允許沙盒內的內容跳轉或導覽頂層瀏覽上下文(即主頁面)。若未使用這些標誌,內嵌頁面將無法改變父頁面的 URL(例如無法透過 window.top.location 將使用者導向其他站點)。allow-top-navigation 完全開放此能力,而 allow-top-navigation-by-user-activation 僅允許由使用者操作觸發的導覽。
    使用建議:一般不建議開啟除非內嵌內容確實需要控制主頁導覽(很少見)。如需開啟,建議使用「僅使用者觸發」的版本以降低風險,確保只有在用戶明確操作時才能跳轉主頁。
  • allow-pointer-lock – 允許沙箱內容使用 Pointer Lock API 來鎖定滑鼠指標。預設禁止此操作。
    使用建議:僅當內嵌內容(如遊戲或互動式應用)需要捕捉滑鼠時才加入。
  • allow-modals – 允許沙箱內容開啟模態對話框,例如 alert()、confirm() 及 prompt() 等。預設沙箱會封鎖這些透過腳本產生的對話框,以防干擾用戶體驗。
    使用建議:除非信任內嵌內容且確定需要使用對話框,否則保持禁用。
  • allow-popups-to-escape-sandbox – 允許由沙箱內開啟的新窗口繼承父頁面的正常(非沙箱)環境。若不使用此標誌,從沙盒中打開的新頁籤或窗戶將繼續套用沙箱限制。
    使用建議:如內嵌的第三方內容含有外部連結或廣告,需要在新窗口中正常運作,才考慮開啟此權限。同時務必審慎評估,因為這等於對沙箱外的頁面解除限制。

上述並非完整列表,但涵蓋了開發中最常用的 sandbox 權限標誌。實際使用時,可以依據嵌入內容的需求組合多個標誌。例如,Twitter 提供的 Tweet 按鈕需要能執行腳本、開啟彈出視窗顯示推文編輯框、提交該表單,且訪客需保持已登入狀態(同源 cookie),因此需要 allow-scripts、allow-popups、allow-forms 以及 allow-same-origin 的組合。原則上,應遵循「最小權限」原則:只添加內容正常運作所需的最低權限,其他不必要的功能一律保持封鎖。接下來一節,我們將探討如果未對 iframe 使用 sandbox 進行這些限制,可能產生的安全問題。

未使用 sandbox 時的跨來源安全風險

在未加沙箱保護的情況下,內嵌的第三方內容將像一般頁面一樣擁有完整的功能,這對網站安全構成多種潛在風險:

  • 跨站指令碼攻擊(XSS):如果將來自不受信任來源的內容直接載入 iframe,該內容中的惡意腳本會正常執行,可能竊取使用者敏感資訊或改寫父頁面的內容。例如,一個攻擊者可能在你嵌入的外部頁面植入惡意 JS,竊取使用者的登入憑證或在父頁面上進行未授權的操作。沒有 sandbox 限制時,這些腳本可以為所欲為地運行。
  • Clickjacking(點擊劫持):iframe 也可能被用來進行點擊劫持攻擊——攻擊者將你的網站以隱藏或透明的方式嵌入在他們自己的頁面中,引誘使用者點擊看似正常的按鈕,實際上點擊的是被遮蔽在下的惡意按鈕。雖然防範 clickjacking 主要依賴服務器端的 X-Frame-Options 或 CSP frame-ancestors 標頭來禁止第三方頁面隨意嵌入你的網站,但若你自己在網站中嵌入外部內容而未限制,其內容也可能試圖透過腳本將使用者導向偽裝的頁面。例如,不受限制的第三方 iframe 可以嘗試调用 window.top.location 將整個主頁導向攻擊者的釣魚網站。沙箱默認會阻止此類行為,避免內嵌內容將使用者劫持到其他網址。
  • 窗口和表單濫用:未沙箱的內嵌內容可以自由地開啟彈窗、跳轉頁面或提交表單。惡意內容可能會不斷彈出廣告窗口、在未經同意下提交隱藏表單(進行 CSRF 攻擊)等,給使用者帶來困擾或安全隱患。相較之下,sandbox 預設禁止了彈窗和表單提交,極大降低了此類濫用風險。
  • 同源攻擊:如果父頁面與 iframe 來自相同網域,且未使用沙箱,那內嵌內容的腳本可以直接存取父頁面的 DOM、Cookies 以及 Web 儲存資料。換言之,它幾乎擁有與你的主頁相同的權限。一旦嵌入的內容被攻擊者控制,便可任意讀取或修改主站資料。這等效於在你的網站引入了一段潛在的惡意程式碼,風險極高。即使是不同來源的 iframe,只要在瀏覽器允許的範圍內,仍可能透過漏洞繞過同源限制或利用 postMessage 傳遞惡意訊息來影響父頁面。如果加上 sandbox,至少可以將其來源隔離並限制其可執行的動作,提供多一道防護。

簡而言之,未受限的 iframe 等於將他人程式碼直接引入你的網站。任何該內容中的漏洞或惡意行為,都可能牽連你的站點及使用者。因此,對於所有非絕對信任的內嵌內容,強烈建議運用 sandbox 等機制進行隔離,以降低「在自家站點引入安全漏洞」的風險。

使用與未使用 sandbox 的行為差異(實作範例)

下面通過一個簡單範例,對比有無 sandbox 時 iframe 行為上的差異。此範例建立兩個 iframe:第一個未使用 sandbox,第二個使用 sandbox(僅允許腳本執行)。兩個 iframe 載入相同的內容,都會嘗試進行以下動作:

  • 存取父頁面的元素並修改其內容(模擬跨來源腳本訪問父頁面資料)。
  • 提交一個表單至外部網站(模擬跨來源表單提交行為)。

透過這兩個 iframe 的表現,我們可以觀察 sandbox 的限制效果。

首先是主頁面 (index.html) 的結構:

index.html
<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <title>Sandbox Demo</title>
  <style>
    iframe { width: 100%; height: 200px; border: 2px solid #ccc; margin: 1em 0; }
  </style>
</head>
<body>

<h3>未使用 sandbox 的 iframe:</h3>
<p id="demo">(父頁面原始文字)</p>
<iframe id="frameNoSandbox"></iframe>

<h3>使用 sandbox(allow-scripts)的 iframe:</h3>
<p>(上方父頁文字將嘗試由沙盒內修改)</p>
<iframe id="frameSandboxed" sandbox="allow-scripts"></iframe>

<!-- 載入 iframe 內容 -->
<script>
const iframeContent = `
  <!DOCTYPE html>
  <html lang="zh-TW">
  <head><meta charset="UTF-8"></head>
  <body>
    <p>Iframe 已載入。</p>
    <button onclick="
      try {
        parent.document.getElementById('demo').innerText = '(內容已被 iframe 修改)';
        document.body.appendChild(document.createElement('p')).innerText = '成功變更父頁面文字!';
      } catch(e) {
        document.body.appendChild(document.createElement('p')).innerText = '無法存取父頁面:' + e.message;
      }">
      嘗試修改父頁面內容
    </button>
    <form action='https://example.com/' target='_blank' method='GET'>
      <p><input type='submit' value='提交表單到 example.com'></p>
    </form>
  </body>
  </html>
`;
document.getElementById('frameNoSandbox').srcdoc = iframeContent;
document.getElementById('frameSandboxed').srcdoc = iframeContent;
</script>

</body>
</html>

說明:上例在主頁面中動態設定了兩個 iframe 的內容(使用 srcdoc 直接內嵌 HTML)。iframe 中的腳本按下按鈕時,會嘗試修改父頁面段落(id 為 demo)的文字。如果成功則在 iframe 內顯示「成功變更父頁面文字!」字樣;如果因權限不足而失敗,則顯示錯誤訊息。在 iframe 中還包含一個指向外部網站的表單(按鈕點擊後提交到 example.com,於新分頁開啟)。

將上述檔案保存後用瀏覽器打開,預期會看到:

  • 未使用 sandbox 的 iframe:按下「嘗試修改父頁面內容」按鈕後,父頁面段落文字將被成功改變(顯示「內容已被 iframe 修改」),表示未沙箱的 iframe 可以存取並操作父頁 DOM。隨後 iframe 自己也顯示「成功變更父頁面文字!」的訊息。接著,嘗試點擊提交按鈕,會發現瀏覽器成功在新分頁打開了 example.com(表單順利送出)。
  • 使用 sandbox 的 iframe:按下修改按鈕後,父頁面維持原狀,而 iframe 內會出現「無法存取父頁面:...」的錯誤訊息,表示沙箱中的腳本被禁止與父頁進行跨頁面互動(因未允許同源,視父頁為不同來源而無法訪問)。同樣地,點擊提交按鈕時,沒有任何反應——沙箱已阻止了表單的提交操作,瀏覽器不會跳轉到新頁面。從中可見,sandbox 在允許部分功能(腳本執行)的同時,仍嚴格限制了跨文件的動作(父頁存取、外部提交等)。

透過這個例子可以直觀地體會到:未沙箱的 iframe 能對宿主頁面產生多大的影響,而加了沙箱限制後這些行為就被攔截。根據需求,我們可以逐項開啟某些權限。例如,如果希望沙盒內的內容也能提交表單,則可在 sandbox 屬性中加上 allow-forms;如果希望允許在新窗口開啟,則加入 allow-popups 或 allow-popups-to-escape-sandbox 等等。重點在於:沙箱預設是「緊縮」的,然後我們再按需「放寬」,以此確保安全和功能的平衡

注意:在父頁與 iframe 同源的情況下,有一個安全陷阱需謹記:絕對不要同時對同源 iframe 開啟 allow-scripts 和 allow-same-origin。因為這等於允許該內嵌頁面在執行腳本的同時,與父頁共享相同的原點。一旦同源且可跑腳本,iframe 內的程式碼便可操作父頁 DOM,甚至呼叫父頁移除對自身的 sandbox 限制!如此一來,沙箱形同虛設,等同沒有任何保護作用。因此,同源情境下應盡量避免這兩個標誌同時使用;若因特殊原因必須這樣做,也務必確保內嵌內容是絕對可信且沒有漏洞。

sandbox 與 Content Security Policy(CSP)的差異與協同

除了在 HTML iframe 標籤上使用 sandbox 屬性外,網站也可以透過 Content Security Policy(內容安全政策,CSP) 來增強跨來源內容的安全性。CSP 是由服務器透過 HTTP Header(或 <meta> 標籤)發送的安全策略,它能細緻地控制網頁可以載入哪些資源、執行哪些腳本,以及哪些外部來源被允許等等。在 CSP 的諸多指令中,有兩項跟 iframe 密切相關:

  • frame-ancestors(或舊版的 X-Frame-Options):這項 CSP 指令用於指定哪些來源的網頁被允許將本頁嵌入為 iframe。若未在允許清單中的網站嘗試嵌入,瀏覽器將拒絕加載。這主要是防禦 clickjacking 的利器,可防止你的網頁被隨意框架在惡意網站中。
  • sandbox 指令:CSP 也提供了 sandbox 設定,作用與 <iframe sandbox> 十分相似。網站可以在回應的 CSP Header 中宣告如 Content-Security-Policy: sandbox allow-forms allow-scripts;,瀏覽器在解析該頁面時就會自行套用沙箱限制。這等於由內容提供者主動要求瀏覽器將自己的頁面以沙箱模式運行,即便未透過 iframe,也會對該頁面的行為施加限制。例如,CSP sandbox 預設也會禁用腳本、表單、彈窗等,除非在指令中加入對應的允許標誌(語法上與 iframe 的 sandbox 屬性類似)。

差異點:iframe 的 sandbox 屬性是由嵌入者(父頁面)控制的:也就是說,你在自己的頁面上嵌入他人的內容時,可以透過 HTML 設定來限制對方。而 CSP sandbox 則是由被嵌入內容自身或其服務器所設定的:內容提供者可以藉由 CSP 來自我約束(或約束被載入時的環境)。兩者可以同時存在,且瀏覽器會套用最嚴格的限制。舉例而言,即使某個第三方內容自己沒有使用 CSP sandbox,只要你在 iframe 上加了 sandbox,它一樣會被沙箱化;反之,如果你沒在 iframe 上設置 sandbox,但對方服務器響應帶有 Content-Security-Policy: sandbox,瀏覽器也會對該內容套用沙箱限制。

協同與最佳實踐:在實際應用中,可以將 sandbox 與 CSP 協同運用,形成雙重防護:

  • 對於嵌入第三方內容:由你來控制的 iframe sandbox 是第一道防線,確保所有載入的外部內容預設被關進沙箱。同時,如果可能,與內容提供者溝通讓他們也在服務端設定 CSP sandbox,或者至少提供明確的 CSP 規則(如禁止內聯腳本、只允許可信來源的資源)。如此即便某層防線失效,另一層仍可提供保護。
  • 對於提供內容供他人嵌入:如果你開發的頁面可能被別的網站以 iframe 引用,建議在你自己的服務器上配置適當的 CSP。例如可使用 frame-ancestors 'self'(或更嚴格地使用 X-Frame-Options: SAMEORIGIN)來限制可嵌入你的網站的來源,防止被隨意框架。同時也可以啟用 CSP 的 sandbox 指令,萬一他人以不受限制的 iframe 嵌入你的頁面,你的 CSP sandbox 依然會為你的內容套上保險帶。

需要注意的是,CSP 的 sandbox 指令無法透過 <meta> 標籤啟用,必須由 HTTP Header 發送。另外,不管 CSP 與否,iframe 的 sandbox 屬性都應視作基線措施:CSP 能做的事情雖然更多(如限制資源來源以防範 XSS),但在瀏覽器普及度和錯綜的配置上,iframe sandbox 通常更直接有效也更容易部署。兩者搭配能在不同層面提供安全性——iframe sandbox 是「點對點」地限制單一框架,CSP 則是「全局性」地規範整個頁面及其外聯內容。綜合運用可以實現Defense-in-Depth(縱深防禦)的效果。

常見誤解:問與答

最後,我們針對 sandbox 屬性一些常見的迷思進行解答,加深對其正確用途的理解:

問:sandbox 屬性會完全封鎖 iframe 中的 JavaScript 嗎?
不全然。默認情況下,加上 sandbox 屬性而未指定 allow-scripts,的確會禁用該框架內的一切腳本執行。但 sandbox 允許開發者白名單部分功能,如果加入了 allow-scripts 標誌,iframe 內的腳本就可以正常執行(仍受其他限制約束,例如不能開新窗口)。因此,是否完全封鎖 JS 取決於你是否在 sandbox 中允許它。最佳實踐是:如非必要保持腳本禁用;若允許腳本也請搭配其它限制(如沒有 allow-same-origin 時,腳本即使執行也無法與外界互動太多)。
問:使用了 sandbox,是否就等同於禁止了一切跨來源(cross-origin)操作?
不是。sandbox 確實改變了 iframe 的同源策略處理:如果沒有 allow-same-origin,則無論該內容原本來自何處,都會被視為一個獨立且不與任何域同源的「虛擬來源」。這意味著沙箱內容無法與父頁或其他窗口進行常規的跨域互動(例如無法讀取父頁資料,document.domain 也不可用)。但是,sandbox 並不影響該內容對外部資源的請求。舉例來說,若允許腳本執行,iframe 中的 JS 仍然可以嘗試透過 fetch 或動態建立 <script> 標籤來載入其他來源的資源,只要瀏覽器同源策略(CORS)允許。換言之,sandbox 不等同於防止任何跨域資料交換,它著重於隔離框架內容的權限,使其就算載入了外域內容,也像是在沙箱中玩耍,碰不到外面的東西。若要全面控管跨域資源載入,仍需要結合 CSP 的策略(例如限制可載入的來源)或服務器的過濾。
問:既然有 sandbox,是不是就不需要其他防禦手段(如 CSP 或 X-Frame-Options)了?
並非如此。Sandbox 提供的是前端層面的隔離機制,而 CSP、X-Frame-Options 等則是從服務端/瀏覽器策略層面提供保護。理想的安全方案應當是多管齊下:該設的 Header 一樣要設,比如 X-Frame-Options/DENY 可防止你的敏感頁面被他站框架(這是 sandbox 所無法做到的,因為 sandbox 控制的是你嵌入別人,而不是別人嵌入你),CSP 可防範跨站腳本和資源載入等。Sandbox 是其中一環,不能取代其它安全策略。將 sandbox 和上述手段配合,才能構築縝密的防護網。

總結

HTML5 的 iframe sandbox 屬性為我們提供了一個強大的工具,能將不受信任的外部內容束縛在嚴格的沙盒環境中運行。透過靈活地運用各種標誌,我們在需要時開啟必要的功能,同時最大程度降低潛在風險。在日常前端開發中,只要涉及嵌入第三方內容或用戶產生內容,都應養成使用 sandbox 的習慣,再搭配 Content Security Policy 等策略,打造多層次的安全防線。希望這篇教學能讓您對 sandbox 屬性與跨來源安全性的最佳實踐有更清晰的認識,在實戰中充分運用這項技術,讓網站既功能豐富又安全可靠

CONTACT US

網站設計報價洽詢

請填寫您的資料,我們將儘快與您聯繫! 為必填