Android證書信任問題與大表哥
0x00 起因
1、近期icloud.com、yahoo.com、apple.com遭遇到大規模劫持
WooYun: Yahoo雅虎在國內訪問遭遇SSL中間人攻擊(被替換為自簽名證照)
2、烏雲平臺、CVE都收到大量有關Android APP信任所有證照的漏洞
WooYun: 國內絕大部分Android APP存在信任所有證照漏洞
3、老外寫有關大表哥的文章中提到MITM時360瀏覽器不提示證照錯誤
之前信任證照問題一直都有被提到,但是普遍不受大家重視,因為這個漏洞是利用是需要場景的:MITM(中間人攻擊 Man-in-the-middle attack)。一般情況下MITM相對其他攻擊是比較少見的,如果有良好的上網習慣如不接入不受信任的網路,那就更少可能受此類攻擊了。但是近期發生的MITM據傳是在核心骨幹網BGP上做了改動所以劫持範圍非常之廣,真是防不勝防呀,你被劫持了麼?
0x01 科普
https&&ssl
為了提高網站的安全性,一般會在比較敏感的部分頁面採用https傳輸,比如註冊、登入、控制檯等。像Gmail、網銀、icloud等則全部採用https傳輸。https/ssl主要起到兩個作用:網站認證、內容加密傳輸和資料一致性。經CA簽發的證照才起到認證可信的作用,所有有效證照均可以起到加密傳輸的作用。
數字證照
主要在網際網路上的用於身份驗證的用途。 安全站點在獲得CA(Certificate Authority數字證照認證機構)認證後,獲得一個數字證照,以此來標識其合法身份的真實性。數字證照主要分為伺服器證照和客戶端證照。伺服器證照(SSL證照)用來進行身份驗證和通訊的加密,客戶端證照主要用於身份驗證和電子簽名。找CA申請證照是要收費的。
自簽名證照
非CA頒發的證照,透過自簽名的方式得到的證照。通常Web瀏覽器會顯示一個對話方塊,詢問您是否希望信任一個自簽名證照。這個是不用花錢的。
中間人攻擊
是指攻擊者與通訊的兩端分別建立獨立的聯絡,並交換其所收到的資料,使通訊的兩端認為他們正在透過一個私密的連線與對方直接對話,但事實上整個會話都被攻擊者完全控制。在中間人攻擊中,攻擊者可以攔截通訊雙方的通話並插入新的內容。在許多情況下這是很簡單的。
0x02 分析
如果自己簡單的實現android webview載入網頁,如果直接訪問可信證照的站點是可以正常顯示,但是如果訪問自簽名的證照的站點就會顯示notfound的頁面。(寫本文時apple.com以及apple.com.cn處於劫持狀態)
logcat會輸出網頁顯示不安全的內容
Web Console:The page displayed insecure content!
功能健全的手機瀏覽器訪問自簽名證照的站點會如下提醒
在PC端如果訪問自簽名證照的站點則會出現如下圖左側的提醒
為解決javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
的異常,開發者往往會採用以下的錯誤解決方案。如此是瀏覽器應用採用此類解決方案,那麼風險就更大了。
覆蓋google預設的證照檢查機制
#!java
class bv
implements X509TrustManager
{
bv(bu parambu) {}
public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) {// Do nothing -> accept any certificates}
public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) {// Do nothing -> accept any certificates}
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
}
信任所有主機名
#!java
public static HttpClient getNewHttpClient() {
try {
//獲得密匙庫
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
//信任所有主機名
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
empty HostnameVerifier
#!java
HostnameVerifier hv = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// Always return true -> Accespt any host names
return true;
}
};
忽略WebView證照錯誤繼續載入
#!java
myWebView.setWebViewClient(new WebViewClient(){
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
// TODO Auto-generated method stub
super.onReceivedError(view, errorCode, description, failingUrl);
}
@Override
public void onReceivedSslError(WebView view,
SslErrorHandler handler, SslError error) {
// TODO Auto-generated method stub
handler.proceed();
}});
其實早在14年2月竊聽風暴: Android平臺https嗅探劫持漏洞文中就有提到android平臺的app因為覆蓋google預設的證照檢查機制(X509TrustManager)之後沒有對證照進行應有的安全性檢查,直接接受了所有異常的https證照,不提醒使用者存在安全風險,也不終止這次危險的連線。文中對證照域名檢查(HostnameVerifier)部分沒有細說。
上文有提到PC版的360瀏覽器訪問被劫持網站居然沒有證照錯誤提示,讓人很不敢相信。加上最近android app 證照問題頻發,猜想是否有可能一些手機瀏覽器也會有此類漏洞了。測試過程中發現360手機瀏覽器、和搜狗瀏覽器存在此風險。
百度和遨遊輕鬆檢測出證照異常
而360和搜狗直接載入進入了被劫持的網站。
反編譯檢視遨遊瀏覽器的程式碼,針對證照異常做了處理
而搜狗瀏覽器則是做了證照信任所有主機名不當處理
關鍵字:checkServerTrusted、setHostnameVerifier、ALLOW_ALL_HOSTNAME_VERIFIER、X509TrustManager、onReceivedSslError
0x03 對比
對主流手機瀏覽器進行了橫向對比,測試物件包括:firefox、chrome、UC瀏覽器、搜狗瀏覽器、百度瀏覽器、360安全瀏覽器、歐鵬瀏覽器、遨遊雲瀏覽器、獵豹瀏覽器。
測試方法:手機訪問https://example.com/,觀察是否有安全提醒。(update:此方法已經無效.)
未做提醒直接載入網頁:360安全瀏覽器、獵豹瀏覽器、搜狗瀏覽器
正常做出安全提醒:firefox、chrome、UC瀏覽器、百度瀏覽器、歐鵬瀏覽器、遨遊雲瀏覽器
0x04 建議
開發者:
1、非瀏覽器app,有錢申請ca證照沒錢在客戶端中新增證照,切勿信任所有證照。
2、瀏覽器app,嚴格按照客戶端校驗伺服器證照流程處理:
- 檢視證照是否過期
- CA是否可靠
- CA的公鑰能否正確解開伺服器證照的CA數字簽名,即證照的簽名值
- 伺服器證照上的域名是否和伺服器的實際域名相匹配
3、建議使用setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER)
一個SSLSocketFactory的example
#!java
java public class SecureSocketFactory extends SSLSocketFactory {
private static final String LOG_TAG = "SecureSocketFactory";
private final SSLContext sslCtx;
private final X509Certificate[] acceptedIssuers;
/**
* Instantiate a new secured factory pertaining to the passed store. Be sure to initialize the
* store with the password using [email protected] java.security.KeyStore#load(java.io.InputStream,
* char[])} method.
*
* @param store The key store holding the certificate details
* @param alias The alias of the certificate to use
*/
public SecureSocketFactory(KeyStore store, String alias)
throws
CertificateException,
NoSuchAlgorithmException,
KeyManagementException,
KeyStoreException,
UnrecoverableKeyException {
super(store);
// Loading the CA certificate from store.
final Certificate rootca = store.getCertificate(alias);
// Turn it to X509 format.
InputStream is = new ByteArrayInputStream(rootca.getEncoded());
X509Certificate x509ca = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
AsyncHttpClient.silentCloseInputStream(is);
if (null == x509ca) {
throw new CertificateException("Embedded SSL certificate has expired.");
}
// Check the CA's validity.
x509ca.checkValidity();
// Accepted CA is only the one installed in the store.
acceptedIssuers = new X509Certificate[]{x509ca};
sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(
null,
new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
Exception error = null;
if (null == chain || 0 == chain.length) {
error = new CertificateException("Certificate chain is invalid.");
} else if (null == authType || 0 == authType.length()) {
error = new CertificateException("Authentication type is invalid.");
} else {
Log.i(LOG_TAG, "Chain includes " + chain.length + " certificates.");
try {
for (X509Certificate cert : chain) {
Log.i(LOG_TAG, "Server Certificate Details:");
Log.i(LOG_TAG, "---------------------------");
Log.i(LOG_TAG, "IssuerDN: " + cert.getIssuerDN().toString());
Log.i(LOG_TAG, "SubjectDN: " + cert.getSubjectDN().toString());
Log.i(LOG_TAG, "Serial Number: " + cert.getSerialNumber());
Log.i(LOG_TAG, "Version: " + cert.getVersion());
Log.i(LOG_TAG, "Not before: " + cert.getNotBefore().toString());
Log.i(LOG_TAG, "Not after: " + cert.getNotAfter().toString());
Log.i(LOG_TAG, "---------------------------");
// Make sure that it hasn't expired.
cert.checkValidity();
// Verify the certificate's public key chain.
cert.verify(rootca.getPublicKey());
}
} catch (InvalidKeyException e) {
error = e;
} catch (NoSuchAlgorithmException e) {
error = e;
} catch (NoSuchProviderException e) {
error = e;
} catch (SignatureException e) {
error = e;
}
}
if (null != error) {
Log.e(LOG_TAG, "Certificate error", error);
throw new CertificateException(error);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return acceptedIssuers;
}
}
},
null
);
setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException {
injectHostname(socket, host);
Socket sslSocket = sslCtx.getSocketFactory().createSocket(socket, host, port, autoClose);
// throw an exception if the hostname does not match the certificate
getHostnameVerifier().verify(host, (SSLSocket) sslSocket);
return sslSocket;
}
@Override
public Socket createSocket() throws IOException {
return sslCtx.getSocketFactory().createSocket();
}
/**
* Pre-ICS Android had a bug resolving HTTPS addresses. This workaround fixes that bug.
*
* @param socket The socket to alter
* @param host Hostname to connect to
* @see <a href="https://code.google.com/p/android/issues/detail?id=13117#c14">https://code.google.com/p/android/issues/detail?id=13117#c14</a>
*/
private void injectHostname(Socket socket, String host) {
try {
if (Integer.valueOf(Build.VERSION.SDK) >= 4) {
Field field = InetAddress.class.getDeclaredField("hostName");
field.setAccessible(true);
field.set(socket.getInetAddress(), host);
}
} catch (Exception ignored) {
}
}
}
使用者:使用安全性較好的app
0x05 參考
http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html
http://developer.android.com/reference/javax/net/ssl/X509TrustManager.html
http://developer.android.com/training/articles/security-ssl.html
http://developer.android.com/reference/org/apache/http/conn/ssl/SSLSocketFactory.html
相關文章
- Mac之解決證書不受信任問題2018-12-27Mac
- SSL證書鏈不受信任是哪裡出了問題2022-12-12
- Android okhttp3.0配置https的自簽證書和信任所有證書2019-03-09AndroidHTTP
- fiddler 證書問題2020-09-14
- win10證書無效如何新增信任證書_win10證書失效新增信任圖文詳解2020-04-20Win10
- IdentityServer4 證書不受信任2020-11-18IDEServer
- win7證書問題2024-10-14Win7
- 極狐GitLab Runner 信任域名證書2024-03-04Gitlab
- 本地測試Http升級到Https(證書信任)2020-10-08HTTP
- harbor映象倉庫證書過期問題2020-09-23
- SSL證書申請問題 – HTTPS SSL 教程2020-03-31HTTP
- android 安裝CA證書2024-07-03Android
- 透過微軟證書伺服器自簽名證書替換VMware vCenter證書:達到域內主機訪問vCenter Server是信任狀態2024-07-24微軟伺服器Server
- 記錄 openssl 證書驗證失敗的詭異問題2021-11-17
- Android序列化問題與思考2020-11-12Android
- root下安卓7以上新增系統信任證書2020-10-19安卓
- windows10系統瀏覽網頁出現證書失效怎麼新增信任證書2019-02-21Windows網頁
- 修改網站後提示證書錯誤,解決SSL證書問題的全面指南2024-12-08網站
- 國科雲SSL證書講堂:SSL證書安裝常見問題盤點2023-10-16
- iOS18,fiddler 證書安裝後在系統證書信任設定內查詢不到2024-09-18iOS
- 如何在極狐GitLab Runner 新增信任快取域名證書2024-03-21Gitlab快取
- SLL證書的好處!能解決什麼問題!2020-05-06
- Xamarin Android使用自簽名證書2021-10-01Android
- 網站安裝DigiCert證書受谷歌瀏覽器信任嗎2022-11-21網站谷歌瀏覽器
- 程式碼簽名證書與SSL證書區別2020-08-03
- Nginx unexpected end of file 配置證書遇到問題,如何解決?2023-02-05Nginx
- Confluence6匯入SSL證書和問題解決2018-08-07
- 【教程】uni-app iOS打包解決profile檔案與私鑰證書不匹配問題2024-03-06APPiOS
- 中科三方:5種導致“SSL證書不被信任”的原因2021-05-07
- iOS企業簽名證書為什麼會出現信任提示2020-10-08iOS
- 受微軟信任的交叉證書將在2021年4月到期2021-03-17微軟
- 申請SSL證書遇到這些問題該怎麼辦2023-05-17
- 根證書過期問題大嗎?有什麼影響2023-03-01
- 關於程式碼簽名證書10個常見問題2020-07-29
- SSL證書產品簡介——SSL證書作用、型別,證書選擇與購買2018-10-11型別
- 分割槽使用與Oracle許可證問題XS2022-03-20Oracle
- ssl證書下載與安裝 – 如何下載ssl證書2020-04-01
- 銳安信證書動態信任簽章有什麼用,如何使用?2022-12-29