這是一張有關標題為 使用 Sourcemap 還原網頁前端原始碼:實際操作與安全考量 的圖片

使用 Sourcemap 還原網頁前端原始碼:實際操作與安全考量

了解如何從 Sourcemap 還原網頁前端的原始碼,適用於 React、Next.js 等常見框架的實際範例分析。

前言

在現代前端開發中,編譯器和相關工具如 Vite、Webpack、Rollup、Parcel 和 esbuild 為構建和打包前端應用程式。這些工具能將所撰寫的程式碼轉換為瀏覽器可執行的 JavaScript 檔案 (.js),還能生成 source map 檔案,這些檔案用於輔助除錯和追蹤問題,幫助開發者更有效地進行程式除錯。

Source maps 的編譯是為了在開發過程中提供便利,使開發者能夠對應原始程式碼編譯後的程式碼。這些檔案通常以 .map 結尾(例如 example.min.js.mapstyles.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

在編譯後,如果有選擇編譯 source maps 的情況下,source maps 資訊會嵌入於 .js 檔案最下方。開發人員工具會自動根據此 map 檔案自動重建原始碼,也就是我們上面所看到的右圖黃色底框的部分。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
!(function () {
  var e = {},
    t = {};
  function n(r) {}

  ...

        A = document.getElementById('root');
      (0, t.H)(A).render((0, f.jsx)(x, {}));
    })();
})();
//# sourceMappingURL=main.XXXX.js.map

Source maps 的分析

我們可以直接下載 main.XXXX.js.map,為一個 JSON 格式。

下面是一個標準的 source map 檔案範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    'version': 3,
    'file': 'static/js/main.XXXX.js',
    'mappings': 'gUA4JA,UA1HkB,SAAHA,GAAyE,GAAG,IAEazE,EAClB,C',
    'sources': [
        'index.tsx',
        'utils/useLazyFetch.js'
        '...'
    ],
    'sourcesContent': [
      'source code of index.tsx',
      'source code of utils/useLazyFetch.js',
    ],
    'names': [
       '_ref',
       'isLazy',
       'isFetch',
       'btnSize',
       'gtmItemId',
       'useRef',
       'target',
       '...',
    ],
    'sourceRoot': ''
}

一個標準的 source map 包含以下主要欄位:

序號欄位名稱說明
1version表示 source map 的版本。這裡顯示的版本號通常是 3,這是當前最常見且現代瀏覽器支援的版本。
2file表示這個 source map 檔案所對應的輸出檔案名稱。在這個例子中,它指向 static/js/main.XXXX.js,這是一個經過壓縮或編譯的 JavaScript 檔案。
3mappings是一個複雜的字串,用來描述壓縮後的程式碼與原始程式碼之間的映射關係。這個欄位告訴瀏覽器每一行、每一列的壓縮程式碼應該對應到原始程式碼中的哪個位置。該字串採用了 VLQ (Variable-Length Quantity,可變長度編碼) 編碼方式,以有效地壓縮和表示這些映射資訊。
4sources是一個陣列,列出了原始程式碼的文件名。在這裡,index.tsxutils/useLazyFetch.js 是原始程式碼的兩個檔案,表示這些檔案被編譯成了最終的 main.XXXX.js。
5sourcesContent是一個陣列,包含了原始碼中每個文件的原始內容。如果存在,這裡會直接顯示原始程式碼內容。即使原始文件無法訪問,開發工具也能從 source map 中取得程式碼。
6names是一個陣列,列出了原始程式碼中用到的所有標識符名稱(如變數、函數名等)。這些名稱會在 mappings 中引用,幫助瀏覽器將壓縮後的名稱還原回原始名稱。
7sourceRoot是原始程式碼的根目錄,用於幫助構建完整的源文件路徑。這裡通常為空字串,表示沒有特定的根目錄。

如何還原 source maps?

有部分工具,可以藉由輸入 main.XXXX.jsmain.XXXX.js.map 還原出原始檔。例如 restore-source-tree 工具,不過我在嘗試過程中並未成功,而且一次性只能回復一個 .map 檔。看起來應該是不支援新版的 source map 版本或是其他錯誤。

相較之下,我更推薦 sourcemap 工具,這是一個用 Go 語言實現的強大工具。只需提供目標網站的網址就能還原網站的前端原始碼。

使用流程非常簡單:安裝 Go → Clone 原始專案 → 編譯專案 → 執行 → 取得原始碼。

安裝 Go

傳統安裝方法: 前往 Go 官方網站 下載並安裝。

Windows 使用 WinGet 進行安裝:

1
winget install --id GoLang.Go

Linux:

1
2
3
4
sudo apt install golang-go

# 或使用 Homebrew, 請先確定已安裝 Homebrew:
brew install go

安裝過程通常不會有太大問題。完成安裝後,可以透過以下命令檢查 Go 的版本來確認安裝是否成功:

1
go version

範例輸出:

1
2
3
4
PS C:\Users\wells> go version
go version go1.23.0 windows/amd64
# 若顯示 Go 的版本號,表示安裝成功。
# 注意: 如果安裝後無法找到 Go,請檢查環境變數設定是否正確。

複製 sourcemap 專案

將以下指令輸入於個人目錄下,進行專案的複製與編譯,執行前請先確定好已安裝 Git

1
2
3
4
5
6
7
8
# 將專案複製到本地端
git clone https://github.com/orsinium-labs/sourcemap.git
# 進入該資料夾
cd sourcemap
# 編譯該專案
go build -o sourcemap .
# Linux 可能還需要將其設為可執行
chmod +x sourcemap

將網頁的 source map 轉為原始碼

於該目錄底下輸入以下指令,便可以將網頁中的 map 轉為原始碼:

1
echo 'https://xxxx.com/' | .\sourcemap --output=./sources

範例輸出:

1
2
3
PS C:\Users\wells\Desktop\sourcemap> echo 'https://xxxx.com/' | .\sourcemap --output=./sources
2024-09-02T15:08:59.313+0800    INFO    sourcemap/main.go:83    running
2024-09-02T15:09:01.147+0800    INFO    sourcemap/main.go:89    finished

我們便可以到 ./sourcemap/sources/xxxx.com 底下看到該網頁的原始碼。此原始程式碼包含資料夾結構、註解、套件資訊等內容。

重建 source map 結果

結論

本文詳細說明了如何利用 .js 檔案與 source maps 還原網頁前端的原始碼。如果 source map 文件暴露在生產環境中,則表示使用者端可以還原其原始碼。

這可以使有心人士找到網頁中的潛在漏洞、API、測試網址,甚至是作者的 Gmail 等資訊(如果它存在於註解中)。因此,確保 source maps 不會意外上傳到生產環境,或在發現後及時移除這些文件,是保護原始碼的重要措施。

此外,雖然移除 source maps 可以有效防止程式碼被輕易還原,但仍需關注程式碼邏輯的安全性。確保程式碼的健全性與安全性,是防止潛在漏洞的關鍵保障。

參考文獻

  1. What are source maps? | web.dev
  2. orsinium-labs/sourcemap: Restore the frontend source code from source maps
主題 Stack 由 Jimmy 設計