mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-01-31 00:00:03 +08:00
feat: #420 implement blacklist to disable unsafe SSL/TLS protocol versions on Android
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 3.2.0
|
||||||
|
|
||||||
|
- Feature #420: implement blacklist feature to disable SSL/TLS versions on Android (thanks mobisys Mobile Informationssysteme GmbH)
|
||||||
|
|
||||||
## 3.1.1
|
## 3.1.1
|
||||||
|
|
||||||
- Fixed #372: malformed empty multipart request on Android
|
- Fixed #372: malformed empty multipart request on Android
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cordova-plugin-advanced-http",
|
"name": "cordova-plugin-advanced-http",
|
||||||
"version": "3.1.1",
|
"version": "3.2.0",
|
||||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<engine name="cordova" version=">=4.0.0"/>
|
<engine name="cordova" version=">=4.0.0"/>
|
||||||
</engines>
|
</engines>
|
||||||
<dependency id="cordova-plugin-file" version=">=2.0.0"/>
|
<dependency id="cordova-plugin-file" version=">=2.0.0"/>
|
||||||
|
<preference name="AndroidBlacklistTlsProtocols" default="SSLv3,TLSv1"/>
|
||||||
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
|
<js-module src="www/cookie-handler.js" name="cookie-handler"/>
|
||||||
<js-module src="www/dependency-validator.js" name="dependency-validator"/>
|
<js-module src="www/dependency-validator.js" name="dependency-validator"/>
|
||||||
<js-module src="www/error-codes.js" name="error-codes"/>
|
<js-module src="www/error-codes.js" name="error-codes"/>
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ public class CordovaHttpPlugin extends CordovaPlugin implements Observer {
|
|||||||
|
|
||||||
this.tlsConfiguration.setHostnameVerifier(null);
|
this.tlsConfiguration.setHostnameVerifier(null);
|
||||||
this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
|
this.tlsConfiguration.setTrustManagers(tmf.getTrustManagers());
|
||||||
|
|
||||||
|
if (this.preferences.contains("androidblacklisttlsprotocols")) {
|
||||||
|
this.tlsConfiguration.setBlacklistedProtocols(
|
||||||
|
this.preferences.getString("androidblacklisttlsprotocols", "").split(",")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "An error occured while loading system's CA certificates", e);
|
Log.e(TAG, "An error occured while loading system's CA certificates", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import javax.net.ssl.TrustManager;
|
|||||||
import com.silkimen.http.TLSSocketFactory;
|
import com.silkimen.http.TLSSocketFactory;
|
||||||
|
|
||||||
public class TLSConfiguration {
|
public class TLSConfiguration {
|
||||||
private TrustManager[] trustManagers;
|
private TrustManager[] trustManagers = null;
|
||||||
private KeyManager[] keyManagers;
|
private KeyManager[] keyManagers = null;
|
||||||
private HostnameVerifier hostnameVerifier;
|
private HostnameVerifier hostnameVerifier = null;
|
||||||
|
private String[] blacklistedProtocols = {};
|
||||||
|
|
||||||
private SSLSocketFactory socketFactory;
|
private SSLSocketFactory socketFactory;
|
||||||
|
|
||||||
@@ -33,6 +34,11 @@ public class TLSConfiguration {
|
|||||||
this.socketFactory = null;
|
this.socketFactory = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBlacklistedProtocols(String[] protocols) {
|
||||||
|
this.blacklistedProtocols = protocols;
|
||||||
|
this.socketFactory = null;
|
||||||
|
}
|
||||||
|
|
||||||
public HostnameVerifier getHostnameVerifier() {
|
public HostnameVerifier getHostnameVerifier() {
|
||||||
return this.hostnameVerifier;
|
return this.hostnameVerifier;
|
||||||
}
|
}
|
||||||
@@ -46,12 +52,7 @@ public class TLSConfiguration {
|
|||||||
SSLContext context = SSLContext.getInstance("TLS");
|
SSLContext context = SSLContext.getInstance("TLS");
|
||||||
|
|
||||||
context.init(this.keyManagers, this.trustManagers, new SecureRandom());
|
context.init(this.keyManagers, this.trustManagers, new SecureRandom());
|
||||||
|
this.socketFactory = new TLSSocketFactory(context, this.blacklistedProtocols);
|
||||||
if (android.os.Build.VERSION.SDK_INT < 20) {
|
|
||||||
this.socketFactory = new TLSSocketFactory(context);
|
|
||||||
} else {
|
|
||||||
this.socketFactory = context.getSocketFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.socketFactory;
|
return this.socketFactory;
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import java.net.InetAddress;
|
|||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
@@ -12,9 +15,11 @@ import javax.net.ssl.SSLSocketFactory;
|
|||||||
public class TLSSocketFactory extends SSLSocketFactory {
|
public class TLSSocketFactory extends SSLSocketFactory {
|
||||||
|
|
||||||
private SSLSocketFactory delegate;
|
private SSLSocketFactory delegate;
|
||||||
|
private String[] blacklistedProtocols;
|
||||||
|
|
||||||
public TLSSocketFactory(SSLContext context) {
|
public TLSSocketFactory(SSLContext context, String[] blacklistedProtocols) {
|
||||||
delegate = context.getSocketFactory();
|
this.delegate = context.getSocketFactory();
|
||||||
|
this.blacklistedProtocols = Arrays.stream(blacklistedProtocols).map(String::trim).toArray(String[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -55,9 +60,18 @@ public class TLSSocketFactory extends SSLSocketFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Socket enableTLSOnSocket(Socket socket) {
|
private Socket enableTLSOnSocket(Socket socket) {
|
||||||
if (socket != null && (socket instanceof SSLSocket)) {
|
if (socket == null || !(socket instanceof SSLSocket)) {
|
||||||
((SSLSocket) socket).setEnabledProtocols(new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" });
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String[] supported = ((SSLSocket) socket).getSupportedProtocols();
|
||||||
|
|
||||||
|
String[] filtered = Arrays.stream(supported).filter(
|
||||||
|
val -> Arrays.stream(this.blacklistedProtocols).noneMatch(val::equals)
|
||||||
|
).toArray(String[]::new);
|
||||||
|
|
||||||
|
((SSLSocket) socket).setEnabledProtocols(filtered);
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,5 @@
|
|||||||
<allow-intent href="itms-apps:*" />
|
<allow-intent href="itms-apps:*" />
|
||||||
</platform>
|
</platform>
|
||||||
<preference name="AndroidPersistentFileLocation" value="Internal" />
|
<preference name="AndroidPersistentFileLocation" value="Internal" />
|
||||||
|
<preference name="AndroidBlacklistTlsProtocols" value="SSLv3,TLSv1" />
|
||||||
</widget>
|
</widget>
|
||||||
|
|||||||
@@ -104,9 +104,17 @@ const helpers = {
|
|||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
},
|
},
|
||||||
|
isTlsBlacklistSupported: function () {
|
||||||
|
if (window.cordova && window.cordova.platformId === 'android') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const messageFactory = {
|
const messageFactory = {
|
||||||
|
handshakeFailed: function() { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: Handshake failed' },
|
||||||
sslTrustAnchor: function () { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
|
sslTrustAnchor: function () { return 'TLS connection could not be established: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
|
||||||
invalidCertificate: function (domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
|
invalidCertificate: function (domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
|
||||||
}
|
}
|
||||||
@@ -1014,8 +1022,7 @@ const tests = [
|
|||||||
before: helpers.setRawSerializer,
|
before: helpers.setRawSerializer,
|
||||||
func: function (resolve, reject, skip) {
|
func: function (resolve, reject, skip) {
|
||||||
if (!helpers.isAbortSupported()) {
|
if (!helpers.isAbortSupported()) {
|
||||||
skip();
|
return skip();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetUrl = 'http://httpbin.org/post';
|
var targetUrl = 'http://httpbin.org/post';
|
||||||
@@ -1036,8 +1043,7 @@ const tests = [
|
|||||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||||
func: function (resolve, reject, skip) {
|
func: function (resolve, reject, skip) {
|
||||||
if (!helpers.isAbortSupported()) {
|
if (!helpers.isAbortSupported()) {
|
||||||
skip();
|
return skip();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
var url = 'https://httpbin.org/drip?duration=2&numbytes=10&code=200';
|
var url = 'https://httpbin.org/drip?duration=2&numbytes=10&code=200';
|
||||||
var options = { method: 'get', responseType: 'blob' };
|
var options = { method: 'get', responseType: 'blob' };
|
||||||
@@ -1064,8 +1070,7 @@ const tests = [
|
|||||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||||
func: function (resolve, reject, skip) {
|
func: function (resolve, reject, skip) {
|
||||||
if (!helpers.isAbortSupported()) {
|
if (!helpers.isAbortSupported()) {
|
||||||
skip();
|
return skip();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
var sourceUrl = 'http://httpbin.org/xml';
|
var sourceUrl = 'http://httpbin.org/xml';
|
||||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||||
@@ -1097,8 +1102,7 @@ const tests = [
|
|||||||
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
expected: 'rejected: {"status":-8, "error": "Request ...}',
|
||||||
func: function (resolve, reject, skip) {
|
func: function (resolve, reject, skip) {
|
||||||
if (!helpers.isAbortSupported()) {
|
if (!helpers.isAbortSupported()) {
|
||||||
skip();
|
return skip();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1148,6 +1152,21 @@ const tests = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'should reject connecting to server with blacklisted SSL version #420',
|
||||||
|
expected: 'rejected: {"status":-2, ...',
|
||||||
|
func: function (resolve, reject, skip) {
|
||||||
|
if (!helpers.isTlsBlacklistSupported()) {
|
||||||
|
return skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
cordova.plugin.http.get('https://tls-v1-0.badssl.com:1010/', {}, {}, resolve, reject);
|
||||||
|
},
|
||||||
|
validationFunc: function (driver, result) {
|
||||||
|
result.type.should.be.equal('rejected');
|
||||||
|
result.data.should.be.eql({ status: -2, error: messageFactory.handshakeFailed() });
|
||||||
|
}
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (typeof module !== 'undefined' && module.exports) {
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
|||||||
Reference in New Issue
Block a user