2015年9月21日 星期一

偵測網路環境是否有 Captive Portal (使用 Wi-Fi 時會先導到登入網頁)

Captive Portal 是指在可以正常用網路前, 先導到一個登入網頁, 登入後才可以正常上網。通常用在需要付費上網的地方, 或是飯店提供免費但需帳號登入的 Wi-Fi。不過不限於 Wi-Fi 連線

這樣作的好處是使用者可以透過網頁看到比較詳細的說明, 像是「使用者同意書」、「使用限制」、「收費標準」等。對於 Wi-Fi 連線的供應者, 在連上 Wi-Fi 時登入 Wi-Fi 帳號和密碼, 就無法提供這些資訊。

但缺點是使用裝置會誤以為有網路可用 (因為 Wi-Fi 層級有通), 但其實還不能用。如果用的 app 沒有作進一步的偵測, 不會開啟登入網頁, 就無法上網了。使用者會覺得「Wi-Fi 有通 app 卻不能用」, 而覺得是 app 的問題。

Android 4.0.1 開始有內建一套偵測機制 [*1], 發現有 Captive Portal 時會通知使用者:

private boolean isCaptivePortal(InetAddress server) {
    HttpURLConnection urlConnection = null;
    if (!mIsCaptivePortalCheckEnabled) return false;

    mUrl = "http://" + server.getHostAddress() + "/generate_204";
    if (DBG) log("Checking " + mUrl);
    try {
        URL url = new URL(mUrl);
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setInstanceFollowRedirects(false);
        urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
        urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
        urlConnection.setUseCaches(false);
        urlConnection.getInputStream();
        // we got a valid response, but not from the real google
        return urlConnection.getResponseCode() != 204;
    } catch (IOException e) {
        if (DBG) log("Probably not a portal: exception " + e);
        return false;
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }
}

作法是送 HTTP request 到 http://clients3.google.com/generate_204 [*2], 然後看 HTTP response code 是否為 204 No Content。另一個作法是連 http://www.google.com/blank.html, 然後檢查 Content-Length 是否為 0。

不過當使用者用 HTTPS 上網時, 這招就不管用了, 連線可能出現 certificate error 或是沒反應。原因是 HTTPS 會先加密連線才傳 HTTP, 提供 Wi-Fi 的 AP 無法偵測到有 HTTP 連線, 自然也就無法重導到登入頁面。這正是 HTTPS 要的結果: 使用者送出的連線不該被任意地導到其它地方 (會讓使用者誤以為那是他連往的頁面)。所以....這招應該會在 HTTPS 普及後而愈來愈少人用吧。另一方面也沒有更好的作法可以讓 Wi-Fi 供給者先顯示訊息再讓使用者登入使用。

備註

1. 5.1.1 r6的程式碼有點不同, 變得更一般化, 可自訂測試用的網址還有將 200 + 無資料視為 204, 不過概念一樣。

2. 原始碼是用 http://clients3.google.com/generate_204, 不過用 http://www.google.com/generate_204 也通

參考資料

2015年9月18日 星期五

Web Proxy 是否能處理 HTTPS 連線?

在討論 HTTPS Proxy 前, 要先分清楚是在講那件事:

  • Proxy 和 client 之間的連線用 HTTPS
  • Proxy 允許 client 透過它往外用 HTTPS 連線

前者的好處是保護 client 和 Proxy 之間的連線, client 若是用 Wi-Fi 的話, 可藉此加密所有送出的 HTTP 連線, 防止有人偷聽 Wi-Fi 連線。

後者是本篇要討論的對象: Web Proxy 是否能支援 client 對外的 HTTPS 連線? 又是如何作到的? 畢竟 HTTPS 是基於 SSL 連線的 HTTP, 既然資料都用 SSL 加密了, Proxy 看不到 HTTP header 和 body, 要怎麼作好 Web Proxy 的角色?

用 HTTP tunnel

Proxy 接受 HTTP 1.1 的 CONNECT method, 讓 client 用 CONNECT 對外打通一個 TCP 連線 (又稱 HTTP tunnel), 然後只作 TCP packet forwarding, 於是 client 要作什麼都可以。比方說用 TCP 連線作 SSL handshake 轉成加密連線, 接著就可以走 HTTPS, SPDY 或 HTTP 2 了。

這個作法下, Proxy server 不知道 client 對外傳的內容, 只能經手封包, 不能作其它事。

置換 HTTPS 的 certificate

Proxy 一樣接受 CONNECT method 建立 HTTP tunnel, 不過在 SSL handshake 時, 換掉目的地 Web Server 回傳的 certificate, 改用 Proxy 自己的。然後分別和 client 和外部 Web Server 完成 SSL handshake, 形成這樣的連線:

  • client <-> Proxy: 用 Proxy 的 certificate 建立 SSL 連線, 使用 key K1。
  • Proxy <-> 外部 Web Server: 用 Web Server 的 certificate 建立 SSL 連線, 使用 key K2。

Proxy 收到 client 的資料先用 K1 解密, 再用 K2 加密傳出去; 資料回來的時候反過來比照辦理。這樣 Proxy 就能得知傳送的內容, 可以作 content filtering (如學校可以擋掉成年人才能看的內容)。

這個作法和 SSL man-in-the-middle attack 一樣, 瀏覽器為了避免這類攻擊, 會檢查 certificate 和對應的 public key 資訊是否一致, 不一致時會跳出警告叫使用者不要繼續使用。

不過在可控制的環境下, IT 部門可以在使用者的電腦上裝上 Proxy 的 certificate, 這樣瀏覽器就不會跳出警告。另一方面, 使用者還是可以點選 HTTPS 的圖示檢查 certificate 資訊, 確認 certificate 是否有被換過, 有的話表示傳輸資料有可能被監聽。有興趣的話可以自己裝 mitmproxy 作一個 transparent proxy 試看看

自己實作 HTTP client 時, 可以用 certificate pinning 擋掉 man-in-the-middle attack, 原理是只相信自己指定的 certificate, 這樣就會忽視攻擊者裝到作業系統的 root CA。

其它討論

藉由了解 Web Proxy 如何處理 HTTPS, 可以得知: 在沒有對使用者裝置動手腳的情況下, Web Proxy 只能放行但不知道內容, 或是依網域名稱決定是否放行 (無法細到網址, 因為那在 HTTP header 裡)。另外, Proxy 可以透過 HTTP 1.1 CONNECT method 傳的參數或是 SSL handshake 傳的 certificate 資訊得知網域名稱。藉此決定是否要放行某些網域的 HTTPS 連線。

雖然 Google 鼓勵大家多用 HTTPS 並且朝向 HTTP 2 邁進, 不過反向思考一下: 用 HTTP 然後自己在 application logic 裡加密資料, 也許可以讓 API 能用在更廣的地方。在特殊的情境下 (比方回傳無法連線的資訊), 或許值得一試?

參考資料

HTTPS Proxy 相關資料

關於 HTTP 1.1 CONNECT method

關於 Certificate Pinning

2015-09-29 更新

Scott 的建議補上 certificate pinning 和 mitmproxy 的資訊。還有刪掉部份內容

2015年9月17日 星期四

用 Ubuntu 14.04 作為 Hotspot

最近需要模擬被擋封包的網路環境, 想想用筆電作為 hotspot 再設 firewall, 應該滿容易的。然後用手機連上 hotspot, 就可以模擬出各種擋封包的情況 (如立即回傳 connection error, 或只是 drop 封包)。

實際操作後的確很容易, 裝好 Ubuntu 14.04 後, 需要的東西都齊了, 內建的 Network Manager 有圖形介面, 點一點就可以分享 Wi-Fi。iPad/iPhone 可以連得上, 但是 Android 不行, 原因是 Ubuntu 預設 hotspot 用 Wi-Fi ad hoc mode, 但是 Android 不支援, 要改用 Wi-Fi infrastructure mode 才行。作法見 How do I create hotspots in Ubuntu 14.04?

其它

  • Android 4.0 後支援 Wi-Fi P2P, 可以讓 Android 裝置互連, 不過還是不支援 Wi-Fi ad hoc mode。
  • 用 Wi-Fi ad hoc mode 的時候, iPad/iPhone 會在 Wi-Fi 列表的「裝置」看到筆電用的 SSID; 若改用 infrastructure mode 的時候, 會在 AP 列表看到筆電用的 SSID。
  • 我有試過用 Mac OS X 作一樣的事, 但是新版 (我用 10.10.4) 內建只能用 pf 作 firewall, 和這東西不熟, 看起來沒 iptables 直覺。另外開啟 Internet Sharing 後 pf 就失效了, 就沒再深入研究。想想以後還是專心用 Linux 就好, 時間有限, 搞熟兩套 OS 的成本太高了。

2015年9月16日 星期三

HTTP POST 使用的編碼格式

三不五時會用到相關的東西, 所以花了點時間查清楚, 本篇重點是參考資料。

用 HTTP 的 POST 的時候, 參數會放在 request body, 然後用 HTTP header Content-Type 決定 request body 編碼方式。

常用的格式 ( Content-Type) 有兩種:

  • application/x-www-form-urlencoded: HTML form 的預設值。和 GET 在 URL 後面帶的參數差不多, 用 URL encoding 編碼 (主要差異是空白字元用 '+' 取代), 適合用在簡單的資料。
  • multipart/form-data: 適合用在 binary data, 上傳檔案時要改用這個, 比較省空間 (檔案內容不需另外編碼)。

application/x-www-form-urlencoded 用 URL encoding 編碼, 遇到大量 binary 資料時, 資料會變大很多 (可能會到三倍)。若先用 base64 [*1] 編碼再套 URL encoding 會好一些 (變成 4/3 倍), 但還是沒有 multipart/form-data 省。

除了上述的格式也可以用 application/json 或其它格式, 不過得看 web server 是否支援。JSON 不支援 binary data, 所以有 binary data 時要先用 base64 編碼, binary 資料一樣會變 4/3 倍大。雖然 base64 會增加 1/3 倍的資料量, 但它簡單易用, 我個人滿喜歡 base64 的設計。

若目的是要壓縮上傳的文字資料, 除了 application layer 自己壓縮資料外, 也可以在 transport layer 作: 使用 HTTP header Content-Encoding: gzip 然後用 gzip 壓縮 request body [*2]。雖然 response 很常用 Content-Encoding: gzip, 但 request 很少這麼用的樣子, 需要改 web server 設定才能作用。如 apache2: 需另外設定 Input Decompression

備註

1. 嚴格來說, 要用 base64url (最後兩個 byte 不同), 對 URL encoding 比較安全。Google API 有用這個, 轉換的時候要留意。

2. 另一個選擇是用 Transfer-Encoding: gzip, 這個作法語意比較正確, 但一來實作不如 Content-Encoding: gzip 常見, 二來 Content-Encoding 是 end-to-end, 不用擔心 Proxy 改變內容, 所以還是用 Content-Encoding 較好。

參考資料

POST Content-Type 相關:

Content-EncodingTransfer-Encoding 相關:

其它:

2015年9月3日 星期四

Mac OS X 雜項常識

備忘用, 持續更新。

硬體列表 (裝置沒反應時可看硬體形號找 driver)

左上角 Apple 圖示 -> About This Mac -> System Report -> Hardware

分享無線網路 (當作 hotspot )

System Preference -> Internet -> Internet Sharing。開啟 Internet Sharing 後, 自訂的 pf rules 會失效。

備忘 nginx 設定

未來的某天應該會需要更了解 nginx, 現階段先隨便備忘用到的東西。

request route

參考資料

用 try_files 設定 URI 對到不同 URI 的順序; 用 rewrite 改寫多種組合到同一位置。兩者都可用來實現 request route。

2015年9月1日 星期二

用 mtr 找出 routing 中有問題的節點

以前只知道用 traceroute 和 ping, 但用 traceroute 看完資訊後也不太清楚情況。用 "traceroute better" 搜了一下, 找到 mtr, 試了一下挺確不錯用。這裡有篇介紹: How To Use Traceroute and MTR to Diagnose Network Issues

備忘指令

  • $ mtr HOSTNAME # 看 realtime 變化
  • $ mtr -w HOSTNAME # 搜集一段時間內的資料

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...