Make Web Request in iOS to Web Server that Accepts Client Certificates - ios

I am writing an iOS app that hits one of our own web servers to leverage data. The IIS web server is publicly-accessible, has valid cryptography certificates, and uses TLS 1.2. To my knowledge, all that is up to snuff with App Transport Security. When making web requests in the app, the request times out, but more interestingly, this message is logged "NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9806)".
This IIS server happens to Accept X509 client certificates for a separate use case that does not involve the mobile app. Changing the setting to Ignore results in the iOS app hitting the server fine. I assume the timeout occurs because the server is prompting the app for a client certificate and it doesn't just respond that it doesn't have one. Note, I don't have the client certificates set to Require. I am not sure how to have the iOS app play nicely and just carry on when prompted by the server for a client certificate.
Is there a way to get this to work in iOS while allowing the IIS server to still Accept client certificates? I don't want to diminish ATS by adding hacky exclusions to info.plist.
I don't think it's relevant, but I am developing the iOS app in Xamarin.IOS in C#. A request goes something like this:
using (var client = new WebClient())
{
client.Headers.Add(Constants.RequestHeaders.RequestId, nonce.RequestId.ToString());
client.Headers.Add(Constants.RequestHeaders.Signature, Convert.ToBase64String(nonce.DigitalSignature));
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var resultJson = client.UploadString("https://foo.com/Api/Register", json);
}
EDIT:
I was able to resolve this issue by using the library ModernHttpClient available via NuGet. The API site accepts client certificates, but does not require them. In most browsers, this results in a one-time prompt by the browser asking you to specify the cert you'd like to use. However, in iOS, making a programmatic web request by default does not bring up a prompt (of course) nor does it inform the server it does not have a cert to provide. Hence, the request simply timed out. With ModernHttpClient, I found a way to set this behavior to automatically resolve.
var handler = new NativeMessageHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
using (var client = new HttpClient(handler))
{ ...
Now it works fine. I'd prefer to not have to include a library just for this very specific purpose, but it works. Perhaps this specific functionality could be distilled into a few lines of code without using the library? Regardless, I'm thankful for ModernHttpClient solving this issue; it's really hacky to change server behavior just to support an iOS quirk.

Can you try change iOS Build setting.
change SSL/TLS implement use * Apple TLS *
change HttpClient implementation use * NSUrlSession *
I had similar problem and using HttpClient to call our public Https API, above setting fixed the issue.

Related

Network Error on iOS emulator from Capacitor App

I am using Angular Capacitor v3 with axios. Receiving a Network Error when making any request to any external server. This is only occurring while emulating with XCode. The request never reaches the server, just returns immediately with status 0.
I cannot repeat this problem locally on Windows, or published website, or Android Studio emulators, or published to android device.
Not sure if I am missing a permission or configuration, but I have tried adding "Local Network Usage", "Location Always and When In Use" permissions and played around with NSAppTransportSecurity settings to the Info.plist.
I do not believe its an issue with the server since it seems like it never even reaches it. But I have a verified HTTPS certificate, hosted by Azure, with valid CORS rules.
I have also tried HttpClient with Angular, same result.
The error occurs immediately and does not give specific information but here is the message:
{"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown
Error","url":"https://jsonplaceholder.typicode.com/users/1","ok":false,"name":"HttpErrorResponse","message":"Http
failure response for https://jsonplaceholder.typicode.com/users/1: 0
Unknown Error","error":{"isTrusted":true}}
After extensive research and rebuilding from scratch, I found this was a CORS related issue but specific to setup with Azure, which was my hosted server. The confusing part was that even external example calls, such as to "http://jsonplaceholder.typicode.com/users/1" still gave the same error, which wouldn't have CORS issues, as far as I understand. If anyone knows why that happens, please let me know.
This was related to the fact that in ios, the http/https schemes cannot be used, instead it uses capacitor. This scheme is not allowed in Azure App Service CORS settings, unless you allow all, but I could not because I needed to enable Access-Control-Allow-Credentials. So I had to build the CORS configuration with the capacitor scheme allowed in my C# app service code. Removing all settings from CORS in Azure App Service then allowed the settings in my C# code to be applied.
I am just facing same issue
It was working on different domain, no hostname is different and it stopped working.
So it doesn't even make a request to a server.
Also same configuration works on android. just not IOS

Ionic app on iOS requires turning off Safari setting "prevent cross-site tracking"

We have an Ionic 4 (Cordova/Angular) app that makes requests to a REST API backend with HttpClient, using cookies for authentication.
The app runs fine on Android (which apparently proves that we've configured CORS correctly). We are now in the process of adapting it for iOS. We have found that when the Safari setting "prevent cross-site tracking" is turned on (which it is by default), WkWebView ignores the Set-Cookie header that is sent by the backend in response to a successful login request, causing all subsequent requests to fail.
Is there a way to avoid the problem without requiring all users to turn off this setting (which is definitely not an option)? We have considered switching to JWT authentication, but I'm afraid that it won't solve the problem, as our backend expects JSON, and according to the docs, "Content-Type: application/json" headers also cause the request to be considered as a CORS request.
Additionaly, we have come across another problem which seems to be related. The app won't run on the iOS simulator on macOS - the Set-Cookie headers are ignored, even if we switch the above setting off! Meaning we can debug the app only on a physical device, which is quite a hindrance.
After several hours of searching on the web, and not finding any similar complaints, I have the feeling that I'm missing something obvious.
This library has come to my rescue: ionic-native-http-connection-backend
Quoting from the docs:
This library adds #ionic-native/http (when available) as a connection backend to Angular's Http and HttpClient
Motivation
Now that Apple promotes/requires the use of WKWebView instead of the deprecated UIWebView, Ionic has switched newly created apps over via their cordova-plugin-ionic-webview (and Cordova offers it via their cordova-plugin-wkwebview-engine). That causes requests that used to work just fine to fail with CORS errors.
The real solution of course is to fix the CORS issues server side - but this may not be possible with e.g. 3rd party APIs.
Even though there is a way to solve CORS issues without changing server's response header by using #ionic-native/http, this only works on device and doesn't provide all the power of Angular's Http and HttpClient.
How it works
The library provides a HttpBackend interface for Angular's HttpClient
This HttpBackend interface tries to use #ionic-native/http whenever it is possible (= on device with installed plugin)
If HttpBackend finds it impossible to use #ionic-native/http, it falls back to standard Angular code (HttpXhrBackend, which uses XmlHttpRequest)
This strategy allows developers to use Angular's HttpClient transparently in both environments: Browser and Device.
Although I still find it strange, that the developer only mentions the library's intended use for situations where CORS can't be configured server-side, but not the 2 problems that it solves for me, namely 1) having to require all users to allow cross-site tracking, and 2) not being able to use the simulator.

Misdirected Request happening on Apple devices

An annoying and persistent issue that I've been facing is a "Misdirected Request Error The client needs a new connection for this request as the requested host name does not match the Server Name Indication (SNI) in use for this connection" when carrying out an AJAX request from my front end to the back end. CORS origin has been taken care of. The weird thing is that this issue only occurs with Apple devices (iPhone/Mac Book) on Safari and Chrome. Other Android and Windows devices work seamlessly. I tried purchasing an additional dedicated SSL certificate to my domain which includes two subdomains (the frontend and backend) from GoDaddy.
Any other tips/solutions please?
Issuing a separate SSL certificate for the two sub-domains did the trick. Make sure that the two sub-domains are not grouped under the same certificate.

iOS Secure Transport TLS extensions

I am developing an application that talks to a peer via a secure channel. The data is secured via Apple's Secure Transport Framework. I am actually trying to remove the TLS extensions from the Client Hello message by configuring the SSLContextRef object but the extensions remained.
Is it possible to remove TLS extensions from the Client Hello message that is sent to the server ? If possible then what are the extensions that can be configured ?
Thanks.
I recently created a code package for handling TLS that takes into account the new TLS restrictions imposed by apple for iOS 13. Here is a link:
https://github.com/eamonwhiter73/IOSObjCWebSockets
With how I structure things, the point in the code you want to change is probably here (if you are using Network.h package):
nw_parameters_configure_protocol_block_t configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL;
nw_parameters_t parameters = nw_parameters_create_secure_tcp(
configure_tls,
NW_PARAMETERS_DEFAULT_CONFIGURATION
);
Instead of a long confusing configure_tls block (like in my code), if you want to disable TLS, you can pass NW_PARAMETERS_DISABLE_PROTOCOL in place of where you would pass the configure_tls block (if you were to configure a secure connection). Hope this helps.

JMeter - How can I use the proxy with secure mobile pages?

I'm using JMeter's proxy to record the HTTP traffic from a mobile app.
It works fine with non secure HTTP requests, but when I try to make a HTTPS request I get an error: "The certificate for this server is invalid" (see screenshot below).
This is of course expected. If I'm on a PC I can simply click on "accept bad certificate" (or something like that) but this isn't an option for my mobile app (I'm testing amazon's app for example).
Is there a way to get my iPhone (or other mobile device) to accept JMeter's certificate?
Is there another way to do this with a REAL mobile device?
edit:
Some of the answers talk about how to modify my app.
I can't modify the app myself - so I need a solution that doesn't require any app modification.
After some research I found this link:
http://nat.guyton.net/2012/01/20/adding-trusted-root-certificate-authorities-to-ios-ipad-iphone/
Which almost works :)
The comments say that in iOS 6 and up using a MD5 doesn't work, and the default key JMeter is using is MD5.
Any thoughts?
Update Feb 13, 2014:
I had given up on this originally, but recently came across an article about using Charles proxy with a real device to capture SSL traffic by adding a certificate to your iPhone. After following the instructions here it works!
http://www.charlesproxy.com/documentation/faqs/ssl-connections-from-within-iphone-applications/
So now I know a solution IS possible, but I'm still stuck on how to get it to work - now using JMeter 2.11 and iOS 7
Thanks
Ophir
These related questions may be helpful:
iphone: secure restfull server "The certificate for this server is invalid
HTTPS Service is not working
HTTPS post request in IOS
I just happened to write an article on that given the new restrictions on iOS 13. In a nutshell:
Generate a certificate.
Import it into your proxy tool (I used OWASP ZAP).
Import the certificate into iOS and add it as a trusted authority.
Access iOS’ proxy settings and point it to your computer.
Full details in the article link below. Hope you find it helpful.
Best regards,
Andre
https://link.medium.com/gcU2SYZtn4

Resources