發現Webpack中洩露的api
已在先知社群發表,轉載請註明出處
1 - 安裝 reverse-sourcemap
需要配置好npm環境 (runoob教程)
使用命令(需要代理) npm install --global reverse-sourcemap
進行安裝
2 - 尋找xxx.js.map
如果有sourcemap的話,在js最後會有註釋:
//# sourceMappingURL=xxxxxxx.js.map
比如這裡我要下載MarketSearch.js.map
(MarketSearch.js
是與站點同名的js,應該是主要的js檔案)
- 在開發者工具中搜尋
.js.map
(位置1) - 找到
MarketSearch.js.map
所在的js (位置2) - 找到對應的鏈
URL
(位置3)- 一般來說,靜態檔案會掛載在當前域名下,但不排除其他站點掛載的情況,所以需要找到對應的
URL
,比如這裡就不同站 - 這裡
MarketSearch.js
的URL
記為http://xxx.xxx/mulu/MarketSearch.js
- 一般來說,靜態檔案會掛載在當前域名下,但不排除其他站點掛載的情況,所以需要找到對應的
3 - 下載xxx.js.map並獲取所有webpack打包檔案
使用curl -O http://xxx.xxx/mulu/MarketSearch.js.map
或者直接訪問http://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 = ./api
,input
則是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安全狗流淚)