發現Webpack中泄露的api

發現Webpack中泄露的api

已在先知社區發表,轉載請註明出處

1 – 安裝 reverse-sourcemap

需要配置好npm環境 (runoob教程)

使用命令(需要代理) npm install --global reverse-sourcemap 進行安裝

2 – 尋找xxx.js.map

如果有sourcemap的話,在js最後會有注釋:

//# sourceMappingURL=xxxxxxx.js.map

比如這裡我要下載MarketSearch.js.mapMarketSearch.js是與站點同名的js,應該是主要的js文件)

  • 在開發者工具中搜索.js.map (位置1)
  • 找到MarketSearch.js.map所在的js (位置2)
  • 找到對應的鏈URL (位置3)
    • 一般來說,靜態文件會掛載在當前域名下,但不排除其他站點掛載的情況,所以需要找到對應的URL,比如這裡就不同站
    • 這裡MarketSearch.jsURL記為//xxx.xxx/mulu/MarketSearch.js

3 – 下載xxx.js.map並獲取所有webpack打包文件

使用curl -O //xxx.xxx/mulu/MarketSearch.js.map

或者直接訪問//xxx.xxx/mulu/MarketSearch.js.map 下載MarketSearch.js.map

使用命令reverse-sourcemap --output-dir ./MarketSearch MarketSearch.js.map即可獲取所有webpack打包文件

4 – 使用IDE/其他編輯器尋找接口

我這裡使用的是vs code

直接使用全局搜索 左邊側邊欄的搜索圖標,或者ctrl+shift+f

4-1 搜索接口

搜索接口有兩個方法:

一個是借鑒先驗請求的url,這種情況需要我們可以訪問到某些接口,比如非SSO的登錄

另一個是直接搜索,這種情況大多是我們沒法訪問到當前站點的接口

4-1-1 借鑒先驗請求的url

比如我們訪問的站點xxx.xxx存在登錄接口,通過嘗試,發現會調用/MarketSearch/api/login接口

那麼我們可以通過不斷刪減來搜索接口/MarketSearch/api/login,/api/login,/login

可以看到,當我們刪減到/api/login的時候,就可以找到接口對應的代碼

這個接口是可以調用的,但是發現其定義的接口與實際訪問的接口不同(第五部分解釋,這裡使用了動態定義的接口)

4-1-2 直接搜索

直接搜索有兩種方法,根據請求方法,或者猜測命名規則進行搜索

4-1-2-1 根據請求方法搜索接口

接口大多是通過get/post方法進行訪問的,所以這是一個很好的關鍵詞

通過請求方法,可以搜索到動態定義的接口(第五部分),避免找不到接口的問題。

而且,如果存在請求方法重寫的代碼,通過請求方法搜索可以發現這些代碼的定義。

post

get

4-1-2-1 根據猜測命名規則搜索接口

一般來說,admin,superadmin,manage之類的關鍵詞比較常見

此外,還可以根據站點名,可調用api命名規則,js命名規則進行搜索。這個站點沒有這樣的接口,就不舉例了。

5 – 尋找動態定義的接口

剛好這個站點存在動態定義的接口(直接明文寫在js代碼中的靜態接口相反):MarketSearch/api/login ,上面我們通過搜索,只發現了/MarketSearch/api/user/login接口,這裡介紹一下如何尋找該接口。

首先搜索login,可以看到在index.ts中對登錄進行了定義

查看index.ts,可以看到這裡定義了用到的視圖,繼續跟蹤Login視圖,命名為Login_1,路徑在./Login

打開Login.tsx,可以看到根據vse_client_1.defaultMainView.Login視圖,創建了對應的元素,vse_client_1定義為vse-client

打開vse-client目錄,尋找defaultMainView視圖的定義

跟蹤Login視圖,可以看到api_1路徑在./api,且Login視圖定義了遊客登錄用戶登錄兩個登錄方式,這裡跟蹤用戶登錄登錄方法

用戶登錄使用了LoginModal模態框

跟蹤LoginModal模態框,可以看到登錄的行為通過yield api_1.api.login(input)來實現

api_1 = ./apiinput則是ItemKey生成的表單中用戶填寫的數據(username & password

跟蹤api_1.api.login

大致說一下這裡的邏輯:

  • key是鍵值,比如這裡調用的是api.login,則key === login

  • 對於每一個vse_share_1.Api定義的接口

  • 如果傳入的key與其中一個接口相同,且不為constructor(通過prototype原型讀取,所以需要排除構造函數),則向下繼續

  • login傳入__awaiter_

  • 通過axios_1.default.post(`/${NAMESPACE}/${vse_share_1.ShareConfig.apiPrefix}/${key}`, args)發起請求

    • ${NAMESPACE} === MarketSearch

      • 查看命名空間的定義

      • 跟蹤../share

    • ${vse_share_1.ShareConfig.apiPrefix} === api

      • 跟蹤vse-share,尋找ShareConfig

    • ${key} === login

    • args === input,即,由ItemKey生成的json{username:"xxx",password:"xxx"}

這種情況下,通過拼接預定義參數和傳入的api名稱,動態生成url路徑,避免了靜態存儲api路徑,使得尋找api接口需要花費的精力大大提升。(web安全狗流淚)