Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-24184] Once HttpClient.validatesSecureCertificate is set to false it can't be set to true

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionNot Our Bug
Resolution Date2016-12-17T20:05:05.000+0000
Affected Version/sn/a
Fix Version/sn/a
ComponentsiOS
Labelsn/a
ReporterAlberto Marcone
AssigneeHans Knöchel
Created2016-11-30T17:35:23.000+0000
Updated2017-03-24T18:57:26.000+0000

Description

I tested this and apparently if you dynamically change the validatesSecureCertificate value, once it's set to false it can't be set to true again. Very weird behavior. It's at application level, not even on the same javascript object. Sample code:
		var httpClient1 = Titanium.Network.createHTTPClient();
		httpClient1.setTimeout(15000);
		httpClient1.validatesSecureCertificate = true;
		httpClient1.open('POST', "https://192.168.2.138");
		httpClient1.onerror = function(e) {
			console.log("#1 onerror",e);
		};
		httpClient1.onload = function(e) {
			console.log("#1 "+this.responseText);
		};
		httpClient1.send();
		
		var httpClient2 = Titanium.Network.createHTTPClient();
		httpClient2.setTimeout(15000);
		httpClient2.validatesSecureCertificate = false;
		httpClient2.open('POST', "https://192.168.2.138");
		httpClient2.onerror = function(e) {
			console.log("#2 onerror",e);
		};
		httpClient2.onload = function(e) {
			console.log("#2 "+this.responseText);
		};
		httpClient2.send();

                var httpClient3 = Titanium.Network.createHTTPClient();
		httpClient3.setTimeout(15000);
		httpClient3.validatesSecureCertificate = true;
		httpClient3.open('POST', "https://192.168.2.138");
		httpClient3.onerror = function(e) {
			console.log("#3 onerror",e);
		};
		httpClient3.onload = function(e) {
			console.log("#3 "+this.responseText);
		};
		httpClient3.send();
the response is the following:
[INFO] :   #1 onerror {
[ERROR] :  = "Il certificato di questo server non \U00e8 valido. \U00c8 possibile che tu stia effettuando la connessione a un server che dichiara di essere \U201c192.168.2.138\U201d, ma che potrebbe mettere a rischio le tue informazioni riservate.";
[INFO] :   code = "-1202";
[ERROR] :  source = "[object TiNetworkHTTPClient]";
[ERROR] :  success = 0;
[ERROR] :  type = error;
[ERROR] :  }
[INFO] :   #2 onerror {
[INFO] :   code = 401;
[ERROR] :  = "HTTP error";
[ERROR] :  source = "[object TiNetworkHTTPClient]";
[ERROR] :  success = 0;
[ERROR] :  type = error;
[ERROR] :  }
[INFO] :   #3 onerror {
[ERROR] :  = "HTTP error";
[INFO] :   code = 401;
[ERROR] :  source = "[object TiNetworkHTTPClient]";
[ERROR] :  success = 0;
[ERROR] :  type = error;
[ERROR] :  }
as you can see the second http call fails with the wrong error (no ssl check)

Comments

  1. Hans Knöchel 2016-11-30

    [~fmerzadyan] Maybe missing accessors like we had previously? Can you check this out?
  2. Alberto Marcone 2016-12-09

    any updates on this? it's a huge security flaw on our app and we would need to fix this asap. Thank you
  3. Gary Mathews 2016-12-16

    I can't reproduce your issue, although I don't have access to the host you are connecting too. Also, if validatesSecureCertificate is not defined it will default to true when deployed to production. validatesSecureCertificate is being set correctly.
       [true, false, true].forEach(function (validate) {
           var http = Titanium.Network.createHTTPClient({
                   onload: function (response) {
                       Ti.API.info('success (validate: ' + validate + ')');
                   },
                   onerror: function (response) {
                       Ti.API.info('error (validate: ' + validate + ')');
                   }
               });
           http.validatesSecureCertificate = validate;
           http.open('GET', 'https://expired.badssl.com/');
           http.send();
       });
       
       error (validate: true)
       error (validate: true)
       success (validate: false)
       
    You could also try httpClient.setValidatesSecureCertificate(true/false) ?
  4. Alberto Marcone 2016-12-16

    First off this has been wrongly tagged as Android although I'm testing it on iOS 9/10. Apparently it's not a matter of expiration but of trusted certificate. Try this (with different responses so that we know exactly the call which generated it):
       var httpClient1 = Titanium.Network.createHTTPClient({validatesSecureCertificate:true});
       httpClient1.open('POST', "https://revoked.grc.com/");
       httpClient1.onerror = function(e) {
       	console.log("#1 onerror",e);
       };
       httpClient1.onload = function(e) {
       	console.log("#1 "+this.responseText);
       };
       httpClient1.send();
       
       setTimeout(function() {
       	var httpClient2 = Titanium.Network.createHTTPClient({validatesSecureCertificate:false});
       	httpClient2.open('POST', "https://revoked.grc.com/");
       	httpClient2.onerror = function(e) {
       		console.log("#2 onerror",e);
       	};
       	httpClient2.onload = function(e) {
       		console.log("#2 OK ");
       	};
       	httpClient2.send();
       }, 2000);
       
       setTimeout(function() {
           var httpClient3 = Titanium.Network.createHTTPClient({validatesSecureCertificate:true});
       	httpClient3.open('POST', "https://revoked.grc.com/");
       	httpClient3.onerror = function(e) {
       		console.log("#3 onerror",e);
       	};
       	httpClient3.onload = function(e) {
       		console.log("#3 OK");
       	};
       	httpClient3.send();
       }, 4000);
       
    RESPONSES:
       [INFO] :   #1 onerror {
       [INFO] :       code = "-1202";
       [INFO] :       error = "The certificate for this server is invalid. You might be connecting to a server that is pretending to be \U201crevoked.grc.com\U201d which could put your confidential information at risk.";
       [INFO] :       source = "[object TiNetworkHTTPClient]";
       [INFO] :       success = 0;
       [INFO] :       type = error;
       [INFO] :   }
       [INFO] :   #2 OK
       [INFO] :   #3 OK
       
    As you can see the last call should give the same error as the first but it doesn't. The params are always specified so it doesn't matter if it's in production or not.
  5. Gary Mathews 2016-12-16

    [~hansknoechel] This is an iOS issue; I've corrected the ticket. Could you take a look?
  6. Hans Knöchel 2016-12-17

    I spent the last day debugging this issue through the whole core and here are my results: - We use the native credentialForTrust: method to ignore the invalid certificate - As soon as the invalid connection *for that domain** is trusted, it won't prompt again (just like in the Browser) - We cannot change this behavior, since it's an internal iOS mechanism So I will resolve this issue as Not Our Bug, I hope that's reasonable after this explanation. Thank you!
  7. Alberto Marcone 2016-12-19

    thank you for the time you took to look into this. At this point would it be possible to remove the cache of the Untrusted SSL Exceptions like someone asked here [http://stackoverflow.com/questions/18120755/how-to-clear-trusted-server-certificate-cache-in-ios] ? Just wondering what my options are at this point
  8. Hans Knöchel 2016-12-19

    I don't see a client-side API for this, so rather your server would need to handle this - or you try to set the cache-control request header, but not sure if that would effect certificates as well.
  9. Lee Morris 2017-03-24

    Closing ticket with reference to the previous comments.

JSON Source