Android網(wǎng)絡(luò)安全配置

基礎(chǔ)概念

HTTP

HTTP協(xié)議工作于 客戶端-服務(wù)端架構(gòu)上。通常,由HTTP客戶端發(fā)起一個(gè)請(qǐng)求,建立一個(gè)到服務(wù)器指定端口(默認(rèn)是80端口)的TCP連接。HTTP服務(wù)器則在那個(gè)端口監(jiān)聽(tīng)客戶端的請(qǐng)求。一旦收到請(qǐng)求,服務(wù)器會(huì)向客戶端響應(yīng)返回一個(gè)狀態(tài),比如"HTTP/1.1 200 OK",以及返回的內(nèi)容,如請(qǐng)求的文件、錯(cuò)誤消息、或者其它信息。

HTTPS

HTTPS 全稱 HTTP over TLS,是對(duì)工作在一加密連接(TLS 或 SSL)上的常規(guī)HTTP協(xié)議的稱呼。

image

TLS 是在傳輸層上層的協(xié)議,應(yīng)用層的下層,作為一個(gè)安全層而存在,翻譯過(guò)來(lái)一般叫做傳輸層安全協(xié)議。

image

TLS 協(xié)議請(qǐng)查看 還沒(méi)寫(xiě)

證書(shū)及認(rèn)證過(guò)程請(qǐng)查看 證書(shū)、CA、證書(shū)信任鏈

對(duì) HTTP 而言,安全傳輸層是透明不可見(jiàn)的,應(yīng)用層僅僅當(dāng)做使用普通的 Socket 一樣使用 SSLSocket 。

TLS是基于 X.509 認(rèn)證,他假定所有的數(shù)字證書(shū)都是由一個(gè)層次化的數(shù)字證書(shū)認(rèn)證機(jī)構(gòu)發(fā)出,即 CA。
TLS 是獨(dú)立于 HTTP 的,任何應(yīng)用層的協(xié)議都可以基于 TLS 建立安全的傳輸通道,如 SSH 協(xié)議。

從抓包說(shuō)起

我們知道使用使用 HTTP網(wǎng)絡(luò)監(jiān)聽(tīng)工具(Fiddler,Charles等),都是需要在客戶端安裝根證書(shū)的(這里涉及證書(shū)的信任鏈,請(qǐng)查看 證書(shū)、CA、證書(shū)信任鏈

而Android系統(tǒng)對(duì)于證書(shū)是分為,系統(tǒng)預(yù)裝證書(shū)用戶安裝證書(shū)

image

Android 6.0(API 級(jí)別 23) 及更低版本,應(yīng)用默認(rèn)信任 系統(tǒng)預(yù)裝證書(shū)和用戶安裝證書(shū)

<base-config cleartextTrafficPermitted="true">
    <trust-anchors>
        <certificates src="system" />
        <certificates src="user" />
    </trust-anchors>
</base-config>

Android 7.0(API 級(jí)別 24) 以后則默認(rèn)僅信任 系統(tǒng)預(yù)裝證書(shū)

<base-config cleartextTrafficPermitted="true">
    <trust-anchors>
        <certificates src="system" />
    </trust-anchors>
</base-config>

cleartextTrafficPermitted 標(biāo)識(shí)是否允許明文傳輸

https://developer.android.com/training/articles/security-config#CustomTrust
所以如果應(yīng)用沒(méi)有做特殊的配置,在 7.0 以上設(shè)備是無(wú)法監(jiān)聽(tīng)到網(wǎng)絡(luò)請(qǐng)求的。

證書(shū)配置

我們可以通過(guò)編輯配置文件來(lái)修改app的默認(rèn)網(wǎng)絡(luò)安全配置

首先,需要 在 manifest 處聲明配置文件

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>

然后就是編輯配置文件,是個(gè)xml文件,數(shù)據(jù)結(jié)構(gòu)格式如下:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </base-config>

    <domain-config>
        <domain>android.com</domain>
        ...
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
        <pin-set>
            <pin digest="...">...</pin>
            ...
        </pin-set>
    </domain-config>
    ...
    <debug-overrides>
        <trust-anchors>
            <certificates src="..."/>
            ...
        </trust-anchors>
    </debug-overrides>
</network-security-config>

  • base-config,默認(rèn)的配置,不在 domain-config 范圍內(nèi)的所有連接所使用的配置。
    • <trust-anchors> 證書(shū)集合,可包裹多個(gè) <certificates> 證書(shū)
  • domain-config。滿足domain規(guī)則所使用的配置,可配置任意多個(gè),domain-config的嵌套表示繼承外層的配置規(guī)則。
    • <trust-anchors> 證書(shū)集合
    • <pin-set> 固定的證書(shū),通過(guò) expiration 配置過(guò)期時(shí)間,可包裹多個(gè) <pin> 證書(shū)
    • <domain> 域名規(guī)則,通過(guò) includeSubdomains 配置是否支持子域名
    • <domain-config> 嵌套規(guī)則
  • debug-overrides。android:debuggable = true使用,會(huì)將這個(gè)節(jié)點(diǎn)下的證書(shū)添加到其他配置上。
    • <trust-anchors> 證書(shū)集合

以上比較重要的是 <certificates><pin> 這里特別提下他們的結(jié)構(gòu)

  • <certificates>
    // src 標(biāo)識(shí)證書(shū)(如果是raw資源則必須以 DER 或 PEM 格式編碼)
    具體轉(zhuǎn)化請(qǐng)點(diǎn)擊查看
    // overridePins 標(biāo)識(shí)是否繞過(guò)證書(shū)固定,默認(rèn)值為 "false",除非在 debug-overrides 有特別指定
<certificates src=["system" | "user" | "raw resource"]
              overridePins=["true" | "false"] />

<pin digest=["SHA-256"]>base64 encoded digest of X.509
    SubjectPublicKeyInfo (SPKI)</pin>

通過(guò)命令可以獲取到 pin

openssl s_client -connect xxx.com:443 -servername xxx.com | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

舉個(gè)栗子,我想debug 模式信任所有證書(shū),但release僅信任系統(tǒng)和特定的證書(shū)

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="system"/>
            <certificates src="@raw/ca_debug_charles"/>
        </trust-anchors>
    </base-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="system"/>
            <certificates src="user"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>

具體請(qǐng)查閱官網(wǎng)驗(yàn)證
https://developer.android.google.cn/training/articles/security-ssl

開(kāi)發(fā)者設(shè)置

這個(gè)時(shí)候我就有需求,我想要release的包是不信任用戶證書(shū)的,但是又需要留個(gè)后門(mén)給自己調(diào)試。

比如:輸入框輸入特定代碼證明我是開(kāi)發(fā)者后,網(wǎng)絡(luò)框架就取消對(duì)證書(shū)的驗(yàn)證,這樣就可以通過(guò)安裝用戶證書(shū)來(lái) 監(jiān)控調(diào)試 release包的https網(wǎng)絡(luò)請(qǐng)求。

代碼使用OkHttp實(shí)現(xiàn),其他網(wǎng)絡(luò)框架請(qǐng)自行g(shù)oogle。


/**
 * 默認(rèn)信任所有的證書(shū)
 */
@SuppressLint("TrulyRandom")
private static SSLSocketFactory createSSLSocketFactory() {
    SSLSocketFactory sSLSocketFactory = null;
    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, new TrustManager[]{new TrustAllManager()},
                new SecureRandom());
        sSLSocketFactory = sc.getSocketFactory();
    } catch (Exception e) {
    }
    return sSLSocketFactory;
}

private static class TrustAllManager implements X509TrustManager {
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}

private static class TrustAllHostnameVerifier implements HostnameVerifier {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
}

  OkHttpClient client = new OkHttpClient.Builder()
          .sslSocketFactory(createSSLSocketFactory())
          .hostnameVerifier(new TrustAllHostnameVerifier())
          .build();

Reference

https://developer.android.com/training/articles/security-config
https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e
https://stackoverflow.com/questions/40404963/how-do-i-get-public-key-hash-for-ssl-pinning
http://www.itdecent.cn/p/59a102f150aa

作者:cchao1024
鏈接:http://www.itdecent.cn/p/dcfb61720413
來(lái)源:簡(jiǎn)書(shū)
簡(jiǎn)書(shū)著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容