以下篇幅將會描述不同前提下對應的除錯策略,當然也有可能不是最優解,望斧正 →_→
前言
何謂「安全域名限制」?
以微信 JS-SDK 的使用為例,每個公眾號被限制最多可設定三個安全域名,且必須能被騰訊伺服器所驗證(這意味著域名必須繫結在一臺可被外網訪問的伺服器上);然後只允許在這幾個域名下才能使用 JS-SDK,這就是安全域名限制。
這種策略應用還相對比較廣泛,大概可以等同於三方提供的 API 需要讓你配置 IP 白名單。
下圖大致描述了 JS-SDK 域名驗證的過程,其中
- WeixinWebview 為微信客戶端內的瀏覽器;
- BuServer 為我們自己的業務伺服器;
- WeixinClient 為微信 App;
以下流程的前提是 BuServer 已經具有簽名所需的 access_token、jsapi_ticket 等資訊。
建立在這個基礎上,我們想繞過這個安全策略,大概有兩種方案:
- 常規操作,將一個固定的域名作為測試專用,填入安全域名列表裡,佔掉一個坑「這邊當然可以專門申請個測試賬號」,然後通過解決內網穿透「ngrok、花生殼之流」便可以實現;
- 非常規操作,通過修改 DNS 解析實現,將特定資源「或所有資源」的請求重寫到本地開發服務上;
常規操作
- 通過 ngrok / 花生殼內網穿透,一般工具都會給你臨時域名,固定的域名需要付費;
- 將域名填入微信安全域名列表中,並將驗證檔案放入 Web 容器裡進行驗證;
- 完成了上面兩步,你就可以愉快的除錯啦;
非常規操作
從上文流程圖中可以看到 JS-SDK config 資訊是通過當前請求頁面的 URL + jsapi_ticket + appid + ...... 按照一定規則進行簽名得到的,然後將這個 config 傳入微信客戶端進行校驗是否合法。
整個驗證過程中,我們能 hack 的是將請求的資源轉發到開發伺服器上,從而能除錯本地的程式碼。
圍繞「將請求的資源轉發到開發伺服器」,可以想到下面兩個方案:
- 將請求攔截,轉發到開發伺服器上;
- 利用 DNS 解析,將安全域名解析到開發伺服器上「俗稱 DNS 劫持」;
方案一 抓包工具轉發
本文不過多贅述,可以通過 Charles,Fiddler 這些抓包工具對指定的請求進行轉發,從而達到目的。
方案二 DNS 劫持
可以先看下圖 DNS 解析過程:
要實現 DNS 劫持,最現實的是通過修改開發機的 hosts 檔案將安全域名解析到本地開發環境 ip 上。
HTTP 請求的劫持
如果你的網站提供的是 HTTP 服務,只需要修改開發機中 hosts 檔案,將安全域名指向開發服務。看到這裡就差不多了,可以自己去實踐了。
HTTPs 請求的劫持
但是目前大多數商業網站都 HTTPs 化了,因此並不能簡單的通過 DNS 劫持,把請求轉發到本地的 HTTP 服務上。
如果你的安全域名是一個 HTTPs 服務,那可能有點麻煩。首先我們來看看一次像 HTTPs Server 請求的時間線:
如上圖可以看到當像 HTTPs Server 發起一個 HTTP 請求時,將會被永久重定向成 HTTPs 請求,然後響應頭中會有一個特殊的頭「strict-transport-security」,這個頭是告知瀏覽器該域下的所有請求都將使用 HTTPs 訪問。因此接下來如果再發起一個 HTTP 的請求,瀏覽器將會內部重定向至 HTTPs 請求。
上文對 HTTPs 請求的描述是為了引出當你安全域名對應的是 HTTPs 服務時,會遇到如下問題:
- 將安全域名 DNS 劫持到本地 HTTP 服務後,在瀏覽器中訪問仍舊是 HTTPs 請求;
- 基於 1 ,我們想到清瀏覽器快取,但是微信開發者工具無法進入 chrome://net-internals/#hsts 清理;
Q1:以 Chrome 為例,進入 chrome://net-internals/#hsts,清除安全策略。
Q2:在微信開發者工具中,我們並不能進入這個頁面,也就意味著如果安全域名在該工具中有安全策略快取,則你無法將它劫持到一個 HTTP 服務,「這邊如果有什麼方案,可以告訴我」。
為了解決這個問題,我們只能引入終極解決方案,通過一個 HTTPs Server 對請求做一個轉發,具體流程如下:
ProxyServer 做了兩件事:
- 配置了安全域名對應的 SSL 證書,支援了 HTTPs 請求;
- 將安全域名下的請求轉發到對應開發服務上;
如果這個 ProxyServer 是作為一個通用服務存在,則需要考慮如何代理到不同開發者啟動的開發服務上。
最開始我們計劃通過 querystring 來制定需要代理到的開發服務的地址,實際情況是一個頁面中所有依賴該域下的請求都得轉發,因此 querystring 的方法不能很方便的實現這個功能。
因此最後通過 cookie 來實現這個需求,通過 cookie 指定開發伺服器地址,這樣該域下的所有資源請求都會帶上該 cookie,就能實現自定義轉發到上游服務了。
最終使用該方案除錯微信 JS-SDK 需要做如下兩步:
- 更改 hosts 檔案,將安全域名劫持到代理服務上;
- 設定 cookie ,指定需要代理到的上游服務;
這樣一來,該代理服務就可以作為通用服務所存在,任何開發者有繞過安全域名限制做一些除錯,都可以使用這個服務。
FAQ
Q1. 常規操作的優劣?
Pros:
- 步驟比較少,容易搞,適合偶爾需要除錯;
Cons:
- 固定域名要錢?啊,不固定域名又要經常改,但是一個月又只有三次修改機會啊;
- 微信安全域名池子沒坑怎麼辦啊。。。「申請個測試賬號也不是不行,但是這樣後端 access_token、jsapi_ticket 相關介面都得修改」;
- 不夠通用,其他三方登入有安全域名設定,並不能複用;
針對 Cons 1 有更優解,可以結合 DNS 劫持,在微信後臺繫結安全域名時使用花生殼驗證域名,然後其實不需要再使用花生殼了,可以修改 hosts,將驗證的域名劫持到本地開發服務,這樣既不用操心 HTTPs 也不用付費要固定域名。有興趣的小夥伴可以嘗試嘗試。
Q2. 非常規操作又有什麼優勢?
Pros:
- 不需要額外的域名,意味著不用改安全域名配置,服務端程式碼也不用動;
- 通用,有類似安全域名策略的三方服務通殺;
- 對於普通開發者來說日常開發除錯成本很低,也不需要什麼特殊許可權;
Cons:
- 搭建流程相對複雜點,需要伺服器資源;
Q3. 真機微信裡怎麼除錯啊?
祭出 Charles,Mac 用 Charles,Win 用 fiddler 開代理,如果是 HTTPs 則還需要配置證書信任,這個步驟本文不再贅述,可自行 Google。
Q4. 怎麼申請測試賬號?
小結
解決方案上,常規方案的額外成本較低,但是在多個開發者協同開發公眾號的情況下,綜合成本較高;非常規方案比較適合多個開發者需要除錯微信 JS-SDK,一勞永逸。
在整個過程中,其實可以瞭解到 DNS 劫持的概念、實際應用,以及對 HTTPs 的進一步瞭解。