前言
在現代前端開發中,編譯器和相關工具如 Vite、Webpack、Rollup、Parcel 和 esbuild 為構建和打包前端應用程式。這些工具能將所撰寫的程式碼轉換為瀏覽器可執行的 JavaScript 檔案 (.js),還能生成 source map 檔案,這些檔案用於輔助除錯和追蹤問題,幫助開發者更有效地進行程式除錯。
Source maps 的編譯是為了在開發過程中提供便利,使開發者能夠對應原始程式碼
與編譯後的程式碼
。這些檔案通常以 .map
結尾(例如 example.min.js.map
或 styles.css.map
)。
通過這些 source maps,開發者可以在除錯時輕鬆追蹤並修正錯誤,確保程式碼在進入生產環境前達到預期效果。
在部署應用程式到生產環境前,通常會對程式碼進行進一步處理,以提升效能和安全性。這些處理包括壓縮程式碼、移除註解以及應用混淆技術。這些步驟不僅減少了檔案大小,加快了應用程式的載入速度,還能使原始程式碼更難以被逆向工程。
例如,將 let isLogin = false
這樣的變數名稱混淆為 let a = 0
,以此保護程式碼的邏輯。
在生產環境(production)中,通常會移除 source maps 以防止有心人士還原原始程式碼。然而,有時 source maps 可能會意外地編譯並與程式碼一起被上傳,這會帶來潛在的安全風險。
本篇文章將說明如何利用這些已經存在於網站上的 source maps 來重建原始程式碼。了解這一過程有助於開發者更好地保護他們的程式碼,並在必要時採取相應措施來防止潛在的安全風險。
分析網頁的 source maps
首先,在目標網頁中按下 F12 打開瀏覽器的開發人員工具(DevTools),然後進入「來源」分頁,找到該網頁所載入的原始碼。
如果該網頁存在 source maps,瀏覽器會自動解析並還原原始碼。左圖顯示了網頁在沒有 source maps 的情況下所公開的程式碼,而右圖則展示了瀏覽器解析並還原後的原始碼。
在編譯後,如果有選擇編譯 source maps 的情況下,source maps 資訊會嵌入於 .js 檔案最下方。開發人員工具會自動根據此 map 檔案自動重建原始碼,也就是我們上面所看到的右圖黃色底框的部分。
|
|
Source maps 的分析
我們可以直接下載 main.XXXX.js.map
,為一個 JSON 格式。
下面是一個標準的 source map 檔案範例:
|
|
一個標準的 source map 包含以下主要欄位:
序號 | 欄位名稱 | 說明 |
---|---|---|
1 | version | 表示 source map 的版本。這裡顯示的版本號通常是 3,這是當前最常見且現代瀏覽器支援的版本。 |
2 | file | 表示這個 source map 檔案所對應的輸出檔案名稱。在這個例子中,它指向 static/js/main.XXXX.js ,這是一個經過壓縮或編譯的 JavaScript 檔案。 |
3 | mappings | 是一個複雜的字串,用來描述壓縮後的程式碼與原始程式碼之間的映射關係。這個欄位告訴瀏覽器每一行、每一列的壓縮程式碼應該對應到原始程式碼中的哪個位置。該字串採用了 VLQ (Variable-Length Quantity,可變長度編碼) 編碼方式,以有效地壓縮和表示這些映射資訊。 |
4 | sources | 是一個陣列,列出了原始程式碼的文件名。在這裡,index.tsx 和 utils/useLazyFetch.js 是原始程式碼的兩個檔案,表示這些檔案被編譯成了最終的 main.XXXX.js。 |
5 | sourcesContent | 是一個陣列,包含了原始碼中每個文件的原始內容 。如果存在,這裡會直接顯示原始程式碼內容。即使原始文件無法訪問,開發工具也能從 source map 中取得程式碼。 |
6 | names | 是一個陣列,列出了原始程式碼中用到的所有標識符名稱(如變數、函數名等)。這些名稱會在 mappings 中引用,幫助瀏覽器將壓縮後的名稱還原回原始名稱。 |
7 | sourceRoot | 是原始程式碼的根目錄,用於幫助構建完整的源文件路徑。這裡通常為空字串,表示沒有特定的根目錄。 |
如何還原 source maps?
有部分工具,可以藉由輸入 main.XXXX.js
與 main.XXXX.js.map
還原出原始檔。例如 restore-source-tree 工具,不過我在嘗試過程中並未成功,而且一次性只能回復一個 .map 檔。看起來應該是不支援新版的 source map 版本或是其他錯誤。
相較之下,我更推薦 sourcemap 工具,這是一個用 Go 語言實現的強大工具。只需提供目標網站的網址就能還原網站的前端原始碼。
使用流程非常簡單:安裝 Go → Clone 原始專案 → 編譯專案 → 執行 → 取得原始碼。
安裝 Go
傳統安裝方法: 前往 Go 官方網站 下載並安裝。
Windows 使用 WinGet 進行安裝:
|
|
Linux:
|
|
安裝過程通常不會有太大問題。完成安裝後,可以透過以下命令檢查 Go 的版本來確認安裝是否成功:
|
|
範例輸出:
|
|
複製 sourcemap 專案
將以下指令輸入於個人目錄下,進行專案的複製與編譯,執行前請先確定好已安裝 Git:
|
|
將網頁的 source map 轉為原始碼
於該目錄底下輸入以下指令,便可以將網頁中的 map 轉為原始碼:
|
|
範例輸出:
|
|
我們便可以到 ./sourcemap/sources/xxxx.com 底下看到該網頁的原始碼。此原始程式碼包含資料夾結構、註解、套件資訊等內容。
結論
本文詳細說明了如何利用 .js
檔案與 source maps
還原網頁前端的原始碼。如果 source map 文件暴露在生產環境中,則表示使用者端可以還原其原始碼。
這可以使有心人士找到網頁中的潛在漏洞、API、測試網址,甚至是作者的 Gmail 等資訊(如果它存在於註解中)。因此,確保 source maps 不會意外上傳到生產環境,或在發現後及時移除這些文件,是保護原始碼的重要措施。
此外,雖然移除 source maps 可以有效防止程式碼被輕易還原,但仍需關注程式碼邏輯的安全性。確保程式碼的健全性與安全性,是防止潛在漏洞的關鍵保障。