本章概述#

大多數使用者透過瀏覽器與網站互動。要建立安全的網站,必須理解瀏覽器如何將 HTML 轉換為互動式的視覺頁面。本章涵蓋瀏覽器的 rendering pipelinebrowser security model,以及駭客如何嘗試繞過這些安全措施。

Web Page Rendering#

瀏覽器中負責將 HTML 轉換為畫面的軟體元件稱為 rendering pipeline,它負責解析 HTML、理解文件結構與內容,並將其轉換為作業系統可繪製的操作。

  • 早期的 HTML 只包含少量樣式資訊(顏色、字型、字體大小),渲染只是依序載入文字和圖片
  • 現代 web 開發者將樣式資訊編碼在獨立的 Cascading Style Sheets (CSS) 檔案中
  • 像 Google Chrome 這樣的現代瀏覽器包含數百萬行程式碼,用於正確解析 HTML 並處理衝突的樣式規則

The Rendering Pipeline 概覽#

高層流程如下:

  1. 瀏覽器收到 HTTP response 後,將 HTML 解析為 Document Object Model (DOM) — 一種記憶體中的資料結構,代表瀏覽器對頁面結構的理解
  2. 對每個 DOM 元素套用樣式規則(styling rules)
  3. 確定頁面結構後,將網頁繪製到螢幕上
  4. 以上過程在極短時間內完成,並在使用者互動時重複執行

瀏覽器在建構 DOM 的同時也會載入並執行遇到的 JavaScript,JavaScript 可以動態修改 DOM 和樣式規則。

The Document Object Model (DOM)#

  • 瀏覽器收到含 HTML 的 HTTP response 後,將 HTML 解析為 DOM
  • DOM 由一系列巢狀的 DOM nodes 組成,每個 node 大致對應原始 HTML 中的一個 tag
  • DOM nodes 可以包含文字內容或其他 DOM nodes,形成分支結構,稱為 DOM tree

外部資源載入#

  • 某些 HTML tag(如 <script><style><image><font><video>)會引用外部 URL
  • 當這些 tag 被解析進 DOM 時,瀏覽器會發起額外的 HTTP request 來載入外部資源
  • 現代瀏覽器會平行執行這些請求以加速頁面載入

容錯機制#

  • DOM 的建構設計得盡可能強健
  • 瀏覽器會自動關閉未關閉的 tag、插入缺失的 tag、忽略損壞的 tag
  • 瀏覽器廠商不會因為網站的錯誤而懲罰使用者

Styling Information#

  • DOM tree 建構完成後,瀏覽器需要決定每個元素如何顯示:前景色、背景色、字型、大小、位置、對齊方式等
  • 開發者偏好將樣式資訊放在獨立的 CSS 檔案中,與 HTML 內容分離,這樣更容易重新設計樣式,也讓 HTML 保持語義化
  • HTML 透過 <style> tag 引用外部 CSS 檔案的 URL

CSS Selectors#

  • 每個 stylesheet 包含 selectors,用於選取 HTML 中的特定 tag 並賦予樣式
  • Selectors 可以很簡單(例如:所有 <h1> tag 的文字為藍色),也可以很複雜(例如:滑鼠懸停時超連結變色)
  • Rendering pipeline 需要大量邏輯來決定最終樣式,因為需要遵守嚴格的優先順序規則

業界用來測試瀏覽器對 web 標準遵從程度的基準測試是 Acid3 test,只有少數瀏覽器能拿到滿分 100。

Figure 3-1: Acid3 瀏覽器標準相容性測試

JavaScript#

  • JavaScript 是一種完整的程式語言,由瀏覽器的 JavaScript engine 在頁面渲染時執行
  • JavaScript 可透過 <script> tag 內嵌於 HTML 中,或更常見地引用外部 JavaScript 檔案
  • 預設情況下,瀏覽器一遇到 <script> tag 就立即執行其中的程式碼

defer 屬性#

  • 如果 JavaScript 嘗試操作尚未存在於 DOM 中的元素,會導致問題
  • <script> tag 加上 defer 屬性可以讓 JavaScript 延遲到整個 DOM 建構完成後再執行

Browser Security Model 與 Sandbox#

瀏覽器會急切地執行遇到的任何 JavaScript,這帶來嚴重的安全隱患。駭客的目標通常是在其他使用者的機器上遠端執行程式碼。因此,現代瀏覽器透過 browser security model 嚴格限制 JavaScript,要求其在 sandbox 中執行。

JavaScript 不被允許的操作:

  • 啟動新程序或存取其他程序
  • 讀取任意系統記憶體(JavaScript 是 managed memory language)
  • 存取本地磁碟(瀏覽器提供的 local storage 是抽象化的,與檔案系統隔離)
  • 存取作業系統的網路層
  • 呼叫作業系統函式

JavaScript 被允許的操作:

  • 讀取和操作當前頁面的 DOM
  • 透過 event listener 監聽和回應使用者動作
  • 代替使用者發起 HTTP 請求
  • 開啟新頁面或刷新當前頁面的 URL(僅限回應使用者動作時)
  • 寫入瀏覽器歷史記錄並進行前進/後退導航
  • 請求使用者的地理位置(需使用者授權)
  • 請求發送桌面通知的權限

即使有這些限制,注入惡意 JavaScript 仍然非常危險。攻擊者可以透過 cross-site scripting (XSS) 讀取使用者輸入的信用卡資料或登入憑證。即使是極少量的注入程式碼也構成威脅,因為注入的程式碼可以在 DOM 中新增 <script> tag 來載入完整的惡意 payload。XSS 防禦將在 Chapter 7 詳述。

Before and After Rendering: 瀏覽器的其他職責#

瀏覽器不僅僅是 rendering pipeline 和 JavaScript engine,還負責許多其他工作:

  • 與作業系統互動以解析和快取 DNS 位址
  • 解讀和驗證 security certificates
  • 在需要時將請求編碼為 HTTPS
  • 根據 web server 的指示儲存和傳送 cookies

實際範例:使用者登入 Amazon#

以下是瀏覽器在使用者造訪 Amazon 時的完整流程:

  1. 使用者在瀏覽器中輸入 www.amazon.com
  2. 瀏覽器解析 domain 為 IP 位址 — 先查詢 OS 的 DNS cache,再查詢 ISP 的 DNS cache,最後查詢權威 DNS server
  3. 用解析到的 IP 位址進行 TCP handshake 建立連線
  4. 建立 TCP session 後,建構 HTTP GET request,TCP 將其拆分為封包送出
  5. HTTP 對話升級為 HTTPS — 瀏覽器與 server 進行 TLS handshake,協商加密方法並交換金鑰
  6. Server 透過安全通道回傳包含 Amazon 首頁 HTML 的 HTTP response,瀏覽器解析並顯示頁面,同時觸發大量額外的 HTTP GET request
  7. 使用者導航到登入頁面,輸入憑證並提交表單,產生一個 POST request
  8. Server 驗證憑證,透過 Set-Cookie header 建立 session,瀏覽器儲存 cookie 並在後續請求中自動附帶

本章總結#

  • 瀏覽器的 rendering pipeline 將 HTML 解析為 DOM、套用 CSS 樣式規則,最後繪製頁面
  • JavaScript<script> tag 中被執行,受到 browser security model 的嚴格限制(sandbox)
  • 瀏覽器除了渲染頁面外,還負責 DNS 解析、安全憑證驗證、HTTPS 加密、cookie 管理等工作
  • 下一章將轉向 HTTP 對話的另一端 — web server