{ "id": "164732", "key": "TIMOB-24184", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [], "resolution": { "id": "11", "description": "Is not a bug in our product", "name": "Not Our Bug" }, "resolutiondate": "2016-12-17T20:05:05.000+0000", "created": "2016-11-30T17:35:23.000+0000", "priority": { "name": "High", "id": "2" }, "labels": [], "versions": [], "issuelinks": [], "assignee": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "updated": "2017-03-24T18:57:26.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10206", "name": "iOS", "description": "iOS Platform" } ], "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.\r\n\r\nSample code:\r\n\t\t{code:javascript|title=example.js|borderStyle=solid}\r\n\t\tvar httpClient1 = Titanium.Network.createHTTPClient();\r\n\t\thttpClient1.setTimeout(15000);\r\n\t\thttpClient1.validatesSecureCertificate = true;\r\n\t\thttpClient1.open('POST', \"https://192.168.2.138\");\r\n\t\thttpClient1.onerror = function(e) {\r\n\t\t\tconsole.log(\"#1 onerror\",e);\r\n\t\t};\r\n\t\thttpClient1.onload = function(e) {\r\n\t\t\tconsole.log(\"#1 \"+this.responseText);\r\n\t\t};\r\n\t\thttpClient1.send();\r\n\t\t\r\n\t\tvar httpClient2 = Titanium.Network.createHTTPClient();\r\n\t\thttpClient2.setTimeout(15000);\r\n\t\thttpClient2.validatesSecureCertificate = false;\r\n\t\thttpClient2.open('POST', \"https://192.168.2.138\");\r\n\t\thttpClient2.onerror = function(e) {\r\n\t\t\tconsole.log(\"#2 onerror\",e);\r\n\t\t};\r\n\t\thttpClient2.onload = function(e) {\r\n\t\t\tconsole.log(\"#2 \"+this.responseText);\r\n\t\t};\r\n\t\thttpClient2.send();\r\n\r\n var httpClient3 = Titanium.Network.createHTTPClient();\r\n\t\thttpClient3.setTimeout(15000);\r\n\t\thttpClient3.validatesSecureCertificate = true;\r\n\t\thttpClient3.open('POST', \"https://192.168.2.138\");\r\n\t\thttpClient3.onerror = function(e) {\r\n\t\t\tconsole.log(\"#3 onerror\",e);\r\n\t\t};\r\n\t\thttpClient3.onload = function(e) {\r\n\t\t\tconsole.log(\"#3 \"+this.responseText);\r\n\t\t};\r\n\t\thttpClient3.send();\r\n{code}\r\n\r\nthe response is the following:\r\n\r\n{code:borderStyle=solid}\r\n[INFO] : #1 onerror {\r\n[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.\";\r\n[INFO] : code = \"-1202\";\r\n[ERROR] : source = \"[object TiNetworkHTTPClient]\";\r\n[ERROR] : success = 0;\r\n[ERROR] : type = error;\r\n[ERROR] : }\r\n[INFO] : #2 onerror {\r\n[INFO] : code = 401;\r\n[ERROR] : = \"HTTP error\";\r\n[ERROR] : source = \"[object TiNetworkHTTPClient]\";\r\n[ERROR] : success = 0;\r\n[ERROR] : type = error;\r\n[ERROR] : }\r\n[INFO] : #3 onerror {\r\n[ERROR] : = \"HTTP error\";\r\n[INFO] : code = 401;\r\n[ERROR] : source = \"[object TiNetworkHTTPClient]\";\r\n[ERROR] : success = 0;\r\n[ERROR] : type = error;\r\n[ERROR] : }\r\n{code}\r\n\r\nas you can see the second http call fails with the wrong error (no ssl check)", "attachment": [], "flagged": false, "summary": "Once HttpClient.validatesSecureCertificate is set to false it can't be set to true ", "creator": { "name": "a.marcone", "key": "a.marcone", "displayName": "Alberto Marcone", "active": true, "timeZone": "Europe/Berlin" }, "subtasks": [], "reporter": { "name": "a.marcone", "key": "a.marcone", "displayName": "Alberto Marcone", "active": true, "timeZone": "Europe/Berlin" }, "environment": "Titanium SDK 6.0 / iOS 10", "comment": { "comments": [ { "id": "402277", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "[~fmerzadyan] Maybe missing accessors like we had previously? Can you check this out?", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-11-30T18:39:20.000+0000", "updated": "2016-11-30T18:39:20.000+0000" }, { "id": "403081", "author": { "name": "a.marcone", "key": "a.marcone", "displayName": "Alberto Marcone", "active": true, "timeZone": "Europe/Berlin" }, "body": "any updates on this? it's a huge security flaw on our app and we would need to fix this asap. Thank you", "updateAuthor": { "name": "a.marcone", "key": "a.marcone", "displayName": "Alberto Marcone", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-12-09T12:00:42.000+0000", "updated": "2016-12-09T12:00:42.000+0000" }, { "id": "403490", "author": { "name": "gmathews", "key": "gmathews", "displayName": "Gary Mathews", "active": true, "timeZone": "America/Los_Angeles" }, "body": "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.\r\n\r\n{code:js}\r\n[true, false, true].forEach(function (validate) {\r\n var http = Titanium.Network.createHTTPClient({\r\n onload: function (response) {\r\n Ti.API.info('success (validate: ' + validate + ')');\r\n },\r\n onerror: function (response) {\r\n Ti.API.info('error (validate: ' + validate + ')');\r\n }\r\n });\r\n http.validatesSecureCertificate = validate;\r\n http.open('GET', 'https://expired.badssl.com/');\r\n http.send();\r\n});\r\n{code}\r\n{code}\r\nerror (validate: true)\r\nerror (validate: true)\r\nsuccess (validate: false)\r\n{code}\r\n\r\nYou could also try {{httpClient.setValidatesSecureCertificate(true/false)}} ?", "updateAuthor": { "name": "gmathews", "key": "gmathews", "displayName": "Gary Mathews", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-12-16T10:23:20.000+0000", "updated": "2016-12-16T11:36:00.000+0000" }, { "id": "403492", "author": { "name": "a.marcone", "key": "a.marcone", "displayName": "Alberto Marcone", "active": true, "timeZone": "Europe/Berlin" }, "body": "First off this has been wrongly tagged as Android although I'm testing it on iOS 9/10.\r\nApparently it's not a matter of expiration but of trusted certificate.\r\n\r\nTry this (with different responses so that we know exactly the call which generated it):\r\n\r\n{code}\r\nvar httpClient1 = Titanium.Network.createHTTPClient({validatesSecureCertificate:true});\r\nhttpClient1.open('POST', \"https://revoked.grc.com/\");\r\nhttpClient1.onerror = function(e) {\r\n\tconsole.log(\"#1 onerror\",e);\r\n};\r\nhttpClient1.onload = function(e) {\r\n\tconsole.log(\"#1 \"+this.responseText);\r\n};\r\nhttpClient1.send();\r\n\r\nsetTimeout(function() {\r\n\tvar httpClient2 = Titanium.Network.createHTTPClient({validatesSecureCertificate:false});\r\n\thttpClient2.open('POST', \"https://revoked.grc.com/\");\r\n\thttpClient2.onerror = function(e) {\r\n\t\tconsole.log(\"#2 onerror\",e);\r\n\t};\r\n\thttpClient2.onload = function(e) {\r\n\t\tconsole.log(\"#2 OK \");\r\n\t};\r\n\thttpClient2.send();\r\n}, 2000);\r\n\r\nsetTimeout(function() {\r\n var httpClient3 = Titanium.Network.createHTTPClient({validatesSecureCertificate:true});\r\n\thttpClient3.open('POST', \"https://revoked.grc.com/\");\r\n\thttpClient3.onerror = function(e) {\r\n\t\tconsole.log(\"#3 onerror\",e);\r\n\t};\r\n\thttpClient3.onload = function(e) {\r\n\t\tconsole.log(\"#3 OK\");\r\n\t};\r\n\thttpClient3.send();\r\n}, 4000);\r\n{code}\r\n\r\nRESPONSES:\r\n{code}\r\n[INFO] : #1 onerror {\r\n[INFO] : code = \"-1202\";\r\n[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.\";\r\n[INFO] : source = \"[object TiNetworkHTTPClient]\";\r\n[INFO] : success = 0;\r\n[INFO] : type = error;\r\n[INFO] : }\r\n[INFO] : #2 OK\r\n[INFO] : #3 OK\r\n{code}\r\n\r\nAs 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.", "updateAuthor": { "name": "a.marcone", "key": "a.marcone", "displayName": "Alberto Marcone", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-12-16T10:55:50.000+0000", "updated": "2016-12-16T10:55:50.000+0000" }, { "id": "403493", "author": { "name": "gmathews", "key": "gmathews", "displayName": "Gary Mathews", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~hansknoechel] This is an iOS issue; I've corrected the ticket. Could you take a look?", "updateAuthor": { "name": "gmathews", "key": "gmathews", "displayName": "Gary Mathews", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-12-16T11:39:26.000+0000", "updated": "2016-12-16T11:39:26.000+0000" }, { "id": "403563", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "I spent the last day debugging this issue through the whole core and here are my results:\r\n- We use the native {{credentialForTrust:}} method to ignore the invalid certificate\r\n- As soon as the invalid connection *for that domain** is trusted, it won't prompt again (just like in the Browser)\r\n- We cannot change this behavior, since it's an internal iOS mechanism\r\n\r\nSo I will resolve this issue as {{Not Our Bug}}, I hope that's reasonable after this explanation. Thank you!", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-12-17T20:05:05.000+0000", "updated": "2016-12-17T20:05:05.000+0000" }, { "id": "403601", "author": { "name": "a.marcone", "key": "a.marcone", "displayName": "Alberto Marcone", "active": true, "timeZone": "Europe/Berlin" }, "body": "thank you for the time you took to look into this.\r\n\r\nAt 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", "updateAuthor": { "name": "a.marcone", "key": "a.marcone", "displayName": "Alberto Marcone", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-12-19T10:06:38.000+0000", "updated": "2016-12-19T10:06:38.000+0000" }, { "id": "403602", "author": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "body": "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. ", "updateAuthor": { "name": "hknoechel", "key": "hansknoechel", "displayName": "Hans Knöchel", "active": true, "timeZone": "Europe/Berlin" }, "created": "2016-12-19T10:16:44.000+0000", "updated": "2016-12-19T10:16:44.000+0000" }, { "id": "415716", "author": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Closing ticket with reference to the previous comments.", "updateAuthor": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-03-24T18:57:26.000+0000", "updated": "2017-03-24T18:57:26.000+0000" } ], "maxResults": 9, "total": 9, "startAt": 0 } } }