CFNetwork

RSS for tag

Access network services and handle changes in network configurations using CFNetwork.

CFNetwork Documentation

Pinned Posts

Posts under CFNetwork tag

153 Posts
Sort by:
Post marked as solved
9 Replies
753 Views
I am working on some study in sending the data from iphone to server, and I want to keep the communication even when my program is working on the background mode. For the details, when my program monitors the beacon' signal with special UUID by Corelocation( in other words, the iphone is taken into the beacon'region), then in Corelocation's handling function, the dataTask of URLSession will be excuted to forward the BLE signal's information to server. Because the beacon sends the BLE signal periodically and continuously, I think the data will be sent from iphone to sever with my program continuously. But now, my experiment result is, when I change the application into background: -sometimes, the dataTask can be kept for several hours or one or two days without any problem -sometimes, the dataTask will be stopped and after several minutes, it will be restarted automatically (really confusing). And in this case, I find the BLE monitoring program is kept to work, only the dataTask communication has been stopped I want to know the reason and the dataTask's action condition of the above phenomenon such as keeping or stoping or restarting the communication. Is there any method to keep the communication between the iphone and server without any interruption? Thanks a lot!
Posted
by
Post not yet marked as solved
1 Replies
191 Views
I am using URLSessionConfiguration.background and uploadTask to upload a file from an iOS app. request.httpMethod = "POST" request.setValue("application/octect-stream", forHTTPHeaderField: "Content-Type") let task = uploadURLSession.uploadTask(with: request, fromFile: fileURL) I'd like to understand how to manage the error handling. How the http errors 4xx or 5xx are handled by the URLSession.uploadTask?
Posted
by
Post marked as solved
2 Replies
243 Views
AppVariant: 1:iPhone12,3:13 Code Type: ARM-64 (Native) Role: Foreground Parent Process: launchd [1] Date/Time: 2022-04-17 12:26:31.2123 +0800 Launch Time: 2022-04-17 12:26:21.7178 +0800 OS Version: iPhone OS 14.2 (18B92) Release Type: User Baseband Version: 2.02.04 Report Version: 104 Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Triggered by Thread: 38 Thread 38 name: Thread 38 Crashed: 0 libsystem_kernel.dylib 0x00000001b884f414 __pthread_kill + 8 1 libsystem_pthread.dylib 0x00000001d4d67b40 pthread_kill + 272 (pthread.c:1388) 2 libsystem_c.dylib 0x0000000194c74b74 abort + 104 (abort.c:110) 3 libsystem_malloc.dylib 0x000000019acfa49c malloc_vreport + 560 (malloc_printf.c:183) 4 libsystem_malloc.dylib 0x000000019acfa740 malloc_zone_error + 104 (malloc_printf.c:219) 5 libsystem_malloc.dylib 0x000000019acdfab0 szone_free + 464 (magazine_malloc.c:0) 6 CoreFoundation 0x000000018b8ed638 __CFStringDeallocate + 192 (CFString.c:1168) 7 CoreFoundation 0x000000018b8ce104 _CFRelease + 248 (CFRuntime.c:2126) 8 CoreFoundation 0x000000018b82f78c -[__NSArrayI dealloc] + 80 (NSCollectionAux.h:70) 9 CoreFoundation 0x000000018b832e60 -[__NSDictionaryI dealloc] + 156 (NSCollectionAux.h:48) 10 CFNetwork 0x000000018bfc5d18 invocation function for block in __CFURLCache::CreateAndStoreCacheNode(__CFURLCacheNode*, _CFCachedURLResponse const*, __CFString const*, _CFURLRequest const*, void const*, bool, bool&) + 928 (AutoTypes.h:36) 11 libdispatch.dylib 0x000000018b552fb8 _dispatch_block_async_invoke2 + 148 (queue.c:527) 12 libdispatch.dylib 0x000000018b544db0 _dispatch_client_callout + 20 (object.m:559) 13 libdispatch.dylib 0x000000018b54c10c _dispatch_lane_serial_drain + 580 (inline_internal.h:2548) 14 libdispatch.dylib 0x000000018b54cc90 _dispatch_lane_invoke + 460 (queue.c:3862) 15 libdispatch.dylib 0x000000018b556d78 _dispatch_workloop_worker_thread + 708 (queue.c:6601) 16 libsystem_pthread.dylib 0x00000001d4d68804 _pthread_wqthread + 276 (pthread.c:2206) 17 libsystem_pthread.dylib 0x00000001d4d6f75c start_wqthread + 8 Thread 38 crashed with ARM Thread State (64-bit): x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x0000000000000000 x4: 0x0000000000000000 x5: 0x0000000000000000 x6: 0x0000000000000001 x7: 0x0000000000000010 x8: 0x00000000000005b9 x9: 0xcde6b451acd55cca x10: 0xcccccccccccccccd x11: 0x000000000000000a x12: 0x0000000000000000 x13: 0x0000000000000039 x14: 0x0000000086c24000 x15: 0x00000001eb0d1b80 x16: 0x0000000000000148 x17: 0x000000016ce27000 x18: 0x0000000000000000 x19: 0x0000000000000006 x20: 0x000000000000cd03 x21: 0x000000016ce270e0 x22: 0x000000016ce26400 x23: 0x000000012957c000 x24: 0x0000000000000000 x25: 0x0000000000000000 x26: 0x000000016b36bc8a x27: 0x000000016ce27000 x28: 0x00000002825710a0 fp: 0x000000016ce26310 lr: 0x00000001d4d67b40 sp: 0x000000016ce262f0 pc: 0x00000001b884f414 cpsr: 0x40000000 esr: 0x56000080 Address size fault
Posted
by
Post marked as solved
2 Replies
195 Views
Our app has "allow arbitrary loads" in the "App Transport Security Settings" and generally allows both "http" and "https" connections. I want to restrict basic authentication usage to secure connections only, what is the best way to do that? I have URLSessionTaskDelegate's:     func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { where I can put the relevant logic, but how do I check if the connection attempt in question is happening over TLS or not? I can check task.currentRequest.url scheme being "http" vs "https", and port being nil vs 80 vs 443, but I hope there is a more robust check.
Posted
by
Post not yet marked as solved
4 Replies
288 Views
https://developer.apple.com/documentation/foundation/urlcache has this: "Although URLCache instance methods can safely be called from multiple execution contexts at the same time, be aware that methods like  cachedResponse(for:) and storeCachedResponse(_:for:) have an unavoidable race condition when attempting to read or write responses for the same request." What does it mean "unavoidable"? If I put a lock (mutex / NSLock, or similar) in my wrappers on top of "cachedResponse" / "storeCachedResponse" would that avoid the mentioned race condition? Also, what do they mean by "the same request"? A few examples below: let url = URL(string: "https://www.apple.com")! let req1 = URLRequest(url: url) let req2 = req1 // perhaps "the same" let req3 = URLRequest(url: url) // "the same"? let req4 = URLRequest(url: req1.url!) // "the same"? let req5 = URLRequest(url: url, cachePolicy: req1.cachePolicy, timeoutInterval: req1.timeoutInterval) // "the same"? let req6 = URLRequest(url: url, cachePolicy: req1.cachePolicy, timeoutInterval: 1234) // "the same"? let req7 = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: req1.timeoutInterval) // "the same"? assert(req1 == req2) assert(req1 == req3) assert(req1 == req4) assert(req1 == req5) assert(req1 == req6) // this is ok assert(req1 == req7) // this fails
Posted
by
Post not yet marked as solved
1 Replies
231 Views
Report I have an application that apparently crashed for one of my users. Does anyone have any idea what might have caused this crash? I tried Reproducing the bug, but I had no success. I don't even know what use case would cause such a crash Looking into some other posts with similar problems, but couldn't find anything close to this issue In the next section, I provided you with some information that might be useful. If anything else is needed, feel free to let me know. Crash details Date May 3, 2022, 6:02:02 PM Device iPhone 11 - iOS 15.4.1 Stack trace Crashed: com.apple.CFNetwork.Connection 0 libsystem_kernel.dylib 0x7b78 __pthread_kill + 8 1 libsystem_pthread.dylib 0x73bc pthread_kill + 268 2 libsystem_c.dylib 0x2051c abort + 168 3 libsystem_malloc.dylib 0x1ca04 _malloc_put + 550 4 libsystem_malloc.dylib 0x1cc9c malloc_zone_error + 100 5 libsystem_malloc.dylib 0x170dc nanov2_allocate_from_block + 568 6 libsystem_malloc.dylib 0x16164 nanov2_allocate + 128 7 libsystem_malloc.dylib 0x16080 nanov2_malloc + 64 8 libsystem_malloc.dylib 0x6020 _malloc_zone_malloc + 156 9 libsystem_blocks.dylib 0x1630 _Block_copy + 64 10 libdispatch.dylib 0x1e2c _dispatch_Block_copy + 32 11 libdispatch.dylib 0x3bf8 _dispatch_block_create + 160 12 libdispatch.dylib 0x71e4 dispatch_block_create_with_qos_class + 256 13 libnetwork.dylib 0xbdac8 nw_connection_async_on_queue + 88 14 libnetwork.dylib 0xe35c0 nw_connection_report_state_with_handler_on_nw_queue + 1136 15 libnetwork.dylib 0xe3e80 nw_connection_set_state_on_nw_queue + 304 16 libnetwork.dylib 0x8c5a4 nw_connection_endpoint_report_on_nw_queue + 21732 17 libnetwork.dylib 0xb24a4 nw_endpoint_handler_report + 304 18 libnetwork.dylib 0x11509c -[NWConcrete_nw_endpoint_resolver startWithHandler:] + 2124 19 libnetwork.dylib 0x79d18 nw_endpoint_handler_path_change + 8984 20 libnetwork.dylib 0xa5798 nw_endpoint_handler_start + 1084 21 libnetwork.dylib 0xab124 __nw_connection_start_block_invoke + 1408 22 libnetwork.dylib 0xd5384 nw_connection_start + 252 23 CFNetwork 0x1b570 CFURLRequestCopyHTTPRequestMethod + 912 24 CFNetwork 0x1c1b0 CFHTTPMessageCopyBody + 536 25 CFNetwork 0x116be0 CFURLDownloadCancel + 77848 26 CFNetwork 0x19464 CFURLRequestSetURL + 10348 27 CFNetwork 0x12100 CFURLCacheSetMemoryCapacity + 8996 28 CFNetwork 0x110b8 CFURLCacheSetMemoryCapacity + 4828 29 CFNetwork 0x184e98 _CFNetworkErrorGetLocalizedDescription + 335348 30 CFNetwork 0x185034 _CFNetworkErrorGetLocalizedDescription + 335760 31 libdispatch.dylib 0x1e68 _dispatch_call_block_and_release + 32 32 libdispatch.dylib 0x3a2c _dispatch_client_callout + 20 33 libdispatch.dylib 0xb124 _dispatch_lane_serial_drain + 668 34 libdispatch.dylib 0xbcb4 _dispatch_lane_invoke + 444 35 libdispatch.dylib 0xcf80 _dispatch_workloop_invoke + 1784 36 libdispatch.dylib 0x16500 _dispatch_workloop_worker_thread + 648 37 libsystem_pthread.dylib 0x10bc _pthread_wqthread + 288 38 libsystem_pthread.dylib 0xe5c start_wqthread + 8
Posted
by
Post not yet marked as solved
1 Replies
195 Views
We started noticing that for some users the app is crashing as soon as it opens. They cannot get past the issue unless the app is removed and re-installed. There are several similar, but related crashes that we are seeing and all pointing to CFNetwork. The main ones we are seeing are CFNetwork _CFURLStorageSessionCopyCache, CFNetwork _CFURLStorageSessionCopyIdentifier these are showing on our firebase crashlytics console.We recently updated some of the code within our app to use Async / Await and there are several API calls that are called on the app startup to get necessary data. The only test device we have with the issue has not used the app for some time before we updated and opened it. On this device, it crashes every time the app opens.On other devices, we do seem to get a crash after opening and closing the app repeatedly, which we think might be the same issue but are not 100% sure as we can't debug the issue when it occurs as it is very sporadic and never seems to occur on the simulator and only after reopening the app several times in a row.Configuration: We have seen this occur on different versions of iOS. A few examples of devices are: the iPhone 6s running iOS 14.8.1 and iPhone 12 Pro running iOS iOS 15.3.1.Xcode version 13.3.1 (13E500a)Steps to Reproduce: For the devices that crash every time, it seems the issue only happens on devices already logged in after updating to the latest version of our app v9.1.5 (306).For devices that don't crash on startup every time we have found that we need to open lots of apps in the background and then open and close our app and usually after about 5 times a crash occurs.
Posted
by
Post not yet marked as solved
2 Replies
229 Views
Hi Apple Engineers, We are making an iOS app which uses NSURLSession for network communication. To provide enterprise customer the ability to monitor network traffic, we want to support proxy settings inside our app. So, we take advantage of the connectionProxyDictionary property of NSURLSessionConfiguration. We set the kCFNetworkProxiesProxyAutoConfigURLString key, and expect requests that are scripted to be proxied to either go through the proxy or fail when proxy is not reachable. This is necessary because we don't want requests to go out without being monitored. But during development we noticed that when proxy server is not reachable, requests don't always fail with kCFErrorHTTPProxyConnectionFailure or kCFErrorHTTPSProxyConnectionFailure (CFNetworkError 306 and 310). More specifically, the behavior we observed are as follows: PAC URL resolution timeout: Task failed with 306/310 PAC URL resolution error: Task bypassed proxy and succeeded PAC file download timeout: Task failed with 306/310 PAC file download error: Task bypassed proxy and succeeded PAC file parse error: Task bypassed proxy and succeeded Proxy server connection timeout: Task failed with 306/310 Proxy server connection error: Task bypassed proxy and succeeded The inconsistency of error handling behavior is confusing, is this a bug? If not, is there a way to configure NSURLSession to always yield error without bypassing the proxy when any of the above happens?
Posted
by
Post not yet marked as solved
0 Replies
253 Views
Hey everyone, we've experienced strange behavior in the iOS system with a GHP profile and the PAC file evaluation when there's no internet connection. The setup: Router is not connected to the internet Device connects to a Wi-Fi provided by the router Device has mobile data disabled Device has a proxy set via GHP with a PAC file URL Device tries to access a website on a local IP address (e.g. 192.168.1.1) PAC file: function FindProxyForURL(url, host) { if (shExpMatch(url, "*:993/*")){ return 'DIRECT';}if (shExpMatch(url, "*:465/*")){ return 'DIRECT';}if (shExpMatch(url, "*:587/*")){ return 'DIRECT';} if (isPlainHostName(host) || shExpMatch(host, "*.local") || isInNet(dnsResolve(host), "10.0.0.0", "255.0.0.0") || isInNet(dnsResolve(host), "172.16.0.0", "255.240.0.0") || isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0") || isInNet(dnsResolve(host), "127.0.0.0", "255.255.255.0")) return 'DIRECT'; return 'PROXY my.proxy.address;DIRECT'; } The result: The device is not able to connect to local addresses, the request times out. Based on the PAC file rules, when accessing the 192.168.1.1 address, the proxy should have been bypassed and it should go directly: isInNet(dnsResolve(host), "192.168.0.0", "255.255.0.0"). However, it seems, that the device is still trying to go via proxy which is unreachable since the router is not connected to the internet. The GHP profile has even the flag to bypass proxy if unreachable enabled: <key>ProxyCaptiveLoginAllowed</key> <true/> <key>ProxyPACFallbackAllowed</key> <true/> If we remove the GHP profile from the device, everything works. And if the device has cellular data enabled, it works as well. This setup is used by a customer that is connecting to such router in elevators for some maintenance, so they usually have no signal there - the cellular interface is not working and from time to time, the webpage is successfully loaded - I assume that the device had a signal for a short period of time. I just wanted to check with you if there's anything we do wrong in the proxy setup before reporting a bug. Right now we're trying to reproduce this behavior with CFNetworkDiagnostics and NetworkDiagnostics profiles installed so we have more logs. Although, we've noticed the following message in the logs: CFNetworkAgent PAC Fetch failed with cached error [NSURLErrorDomain:-1009] Have anyone experienced something similar? Thanks in advance!
Posted
by
Post marked as solved
3 Replies
296 Views
Apple team I tried to implement the ssl pinning in iOS through info.plist using Pinned Domains Identity Pinning as found in the official apple blog: How to configure server certificates for your app https://developer.apple.com/news/?id=g9ejcf8y news. As of now i have done the following changes something similar in info.plist : And in code i have used simple URLSession as shown: "https://wang.greenhub.example.org/sites/......./logo.png") else { return } // URL session that doesn't cache. let urlSession = URLSession(configuration: URLSessionConfiguration.ephemeral) let task = urlSession.dataTask(with: imageUrl) { imageData, response, error in DispatchQueue.main.async { // Handle client errors if let error = error { self.HandleClientConnectionError(error: error) return } // Handle server errors guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { self.HandleServerError(response: response!) return } self.AddImageToView(imageData: imageData!) } } task.resume() FYI we have api with multiple subdomains and thus according to the NSIncludesSubdomains documentation here says: it doesn’t apply to the subdomains advanced.math.example.com or ancient.history.example.com because those subdomains have two additional path components. Also it prohibits the use of wild cards so even if i tried to use *.example.org overall the SSL pinning does not seems to work in case of multiple subdomains scenario like mine even if i replace the SHA256-BASE64 pin with wrong ones. Can anyone from apple suggest a solution for this or tell how can we use NSIncludesSubdomains find a solution for pinning against multiple subdomains
Posted
by
Post not yet marked as solved
1 Replies
190 Views
As part of a security assessment, we discovered that our app is storing sensitive information in the cache. After some research and considering the recommendations, we decide to use .ephemeral configurations for our URLSessions, so far so good. However, taking a look at the content in the cfurl_cache_receiver_data table, we realized that some of those responses are not part of any of our endpoints, meaning that this is probably coming from requests made for third-party libraries that we use and we were able to confirm this by using a tool to inspect network traffic. My understanding is the cache mechanism is attached to a URLSessionConfiguration and, at the same time, this configuration is tied to a URLSession. Since this is not our code and therefore different URLSessions it makes sense that this caches the responses for any request. Please correct me if this is wrong. I am wondering if there is a way to disable the caching across the app (including requests made for third-party libraries?) or if there is a different/better approach to this? I am attaching a screenshot of the cfurl_cache_receiver_data table content. For example: token=[value]. This is not the response of one of our endpoints, but an external one. Thanks in advance! PD: Is there a way to easily map the data in the cache.db with the particular endpoint/request?
Posted
by
Post marked as solved
1 Replies
284 Views
Hi everyone, i'm developing an app with Flutter and i've a problem with internet connectivity. Particularly, with the WIFI connection every work well, but when i turn off wifi and active the mobile connection 4G the app does not work and put out this errors: Runner[1930:178958] Connection 1: encountered error(1:50) Runner[1930:178953] Task <291B2B98-3FAE-47ED-BA1C-39F75580220C>.<1> HTTP load failed, 0/0 bytes (error code: -1009 [1:50]) Runner[1930:178960] Task <291B2B98-3FAE-47ED-BA1C-39F75580220C>.<1> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x282f9c210 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_NSURLErrorNWPathKey=unsatisfied (Denied over cellular interface), interface: pdp_ip0, ipv4, dns, expensive, _kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <291B2B98-3FAE-47ED-BA1C-39F75580220C>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(   "LocalDataTask <291B2B98-3FAE-47ED-BA1C-39F75580220C>.<1>" ), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://graph.facebook.com/v13.0, NSErrorFailingURLKey=https://graph.facebook.com/v13.0, _kCFStreamErrorDomainKey=1} How to fix it? Forgive me if I have not used the forum to its fullest, but I am a new user. :) Thanks in advance to those who will help me!
Posted
by
Post not yet marked as solved
1 Replies
207 Views
The parameters of the completion handler are (data: Optional<Data>, urlResponse: Optional<Data>, error: Optional<Error>). Here is an exhaustive switch statement of the parameter combinations: switch (data, ulrResponse, error) {       case (.some, .some, .some):       case (.some, .some, .none):       case (.some, .none, .some):       case (.some, .none, .none):       case (.none, .some, .some):       case (.none, .none, .some):       case (.none, .some, .none):       case (.none, .none, .none): } Which combinations of values may be provided and which will not? For example, I think it's fair to say that both (.some, .none, .some) and (.some, .none, .none) are invalid because a response would have to be provided in order to receive data to pass to the closure, and the documentation states: If a response from the server is received, regardless of whether the request completes successfully or fails, the response parameter contains that information.
Posted
by
Post not yet marked as solved
2 Replies
259 Views
My app is working fine on all devices except on one specific device iPhone 12 Pro Max, that give error "502 Bad Gateway". App is unable to connect to server, what could be the reason for this, is this related to firewall or VPN? This happens only on a single device of the client but on our local testing devices it works perfectly fine. Please do share your thoughts as this issue seems rather unexpected where the app on all other devices is working fine except for one. Thank. you
Posted
by
Post not yet marked as solved
0 Replies
207 Views
I often get questions about disabling the HTTPS default server trust evaluation done by NSURLSession. My general advice is “Don’t do that!” However, there is one case where you have to do that, namely when dealing with a hardware accessory on the local network. This post contains my advice on how to deal with that situation. IMPORTANT If you found your way here and you’re not developing a network-based accessory, I have two suggestions for you: If you’re trying to override HTTPS server trust evaluation for testing purposes, see QA1948 HTTPS and Test Servers. If not, post a question here on DevForums and we’ll try to get you heading in the right direction. Tag it with Security, and either CFNetwork or Network depending on which API you’re using. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" TLS For Accessory Developers Communicating securely with a network-based accessory is tricky because the standard network security protocol, TLS, was designed for the wider Internet, where servers have a stable DNS name. A devices on your local network doesn’t have a stable DNS name. The DNS name my-waffle-maker.local. might resolve to your waffle maker today, but tomorrow it might resolve to a different waffle maker or, worse yet, a malicious waffle varnisher. I guarantee that you won’t like them waffles! Note If you’re unfamiliar with TLS in general, I encourage you to read TLS for App Developers first. TLS puts the S into HTTPS. This post focuses on URLSession and HTTPS, because that’s where this most commonly crops up, but similar logic applies to other networking APIs. Doing the Minimum: The SSH Approach Many accessory firmware developers support TLS by generating a self-signed digital identity on the accessory and using that for their HTTPS server. IMO they should do better — the subject of later sections in this post — but sometimes you have to work with what you’ve got. If you’re working with an accessory like this, and you can’t convince the firmware developer to do better, I recommend that you adopt the SSH approach. In this approach you accept the certificate offered by the accessory the first time and then, when you connect again in the future, check the accessory still has the same certificate. Note I call this the SSH approach because that’s how many folks use SSH. To implement the SSH approach, first disable App Transport Security for local networking by setting the NSAllowsLocalNetworking property. On modern systems this isn’t strictly necessary but, as it says in the docs, this is a good “declaration of intent”. Next, override HTTPS server trust evaluation as shown below: var expectedCertificate: SecCertificate? = nil func shouldAllowHTTPSConnection(trust: SecTrust) async -> Bool { guard let chain = SecTrustCopyCertificateChain(trust) as? [SecCertificate], let actual = chain.first else { return false } guard let expected = self.expectedCertificate else { // A. First connection self.expectedCertificate = actual return true } // B. Subsequent connections return CFEqual(expected, actual) } func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) { switch challenge.protectionSpace.authenticationMethod { case NSURLAuthenticationMethodServerTrust: let trust = challenge.protectionSpace.serverTrust! guard await self.shouldAllowHTTPSConnection(trust: trust) else { return (.cancelAuthenticationChallenge, nil) } let credential = URLCredential(trust: trust) return (.useCredential, credential) default: return (.performDefaultHandling, nil) } } There are a few things to note here: The snippet uses the new Swift async/await syntax. That simplifies the code and it also avoids a gotcha with the old completion-handler approach, where it was easy to forget to call the completion handler. Having said that, you can achieve the same result without using Swift async/await, or indeed, in Objective-C. The authentication challenge handler only deals with HTTPS server trust challenges (NSURLAuthenticationMethodServerTrust). Critically, if it finds a challenge it doesn’t recognise, it resolves that in the default way (.performDefaultHandling). The shouldAllowHTTPSConnection(trust:) method checks to see if it’s connected to the server before. If not, it defaults to allowing the connection (case A). If so, it checks that the certificate hasn’t changed (case B). In a real app you would persist expectedCertificate so that this logic continues to apply the next time your app is launched. When you do that, store the certificate using a key that uniquely identifies the accessory. Do not use the DNS name or IP address because that can change. The first time you connect (case A) you might take other steps to confirm the identity of the accessory. This is something I’ll cover below. The thing to note here is that this function is async, so you can take your time doing that confirmation. For example, you could bounce over to the main actor and display a UI. Doing Better: Add a Side Channel The above represents the absolute minimum you should do; anything less is doing the user a disservice. Aim to do better! Doing better requires some sort of side channel so that the user can confirm the identity of the accessory. For example: If the accessory has a display, you might present a UI asking the user to confirm that the code in your UI matches the one on the accessory’s display. If the accessory has a LED, it might blink that during this initial setup process. If the accessory has a serial number, you might ask the user to confirm that. Or scan that via a barcode. Note For an example of the barcode approach, see Matt’s Configuring a Wi-Fi Accessory to Join the User’s Network sample. This sample uses a different technique for TLS, namely TLS-PSK. This has some advantages — most notably, it avoids messing around with certificates — but I’m not discussing it here because it’s not supported by URLSession. If you adopt this approach make sure that you present a UI that the user can understand. If your UI contains raw certificate details, most users will just say “Sure, whatever.” and click OK, at which point you might as well have saved everyone’s time by accepting the connection on their behalf. Take care not to blindly trust any information you get from the accessory. After all, the goal here is to check the identity of the accessory, and so you mustn’t trust it before completing that check. Sometimes this can be quite subtle. Consider the accessory-with-a-display example above. In that case the accessory’s self-signed certificate might contain a common name of Acme Waffle Maker CCC, where CCC is the code showing on its display. Don’t display the entire string to the user. The presence of Acme Waffle Maker might lead the user to believe that the accessory is valid, even when it isn’t. After all, a malicious waffle varnisher can just as easily create a self-signed certificate with that common name. Rather, extract the code (CCC) and display just that. That way the user will focus on what’s important. Doing Even Better: Proper Security If the accessory’s firmware and hardware developers are on board, there are steps you can take to properly secure your communication with the accessory: Create a custom certificate authority (CA) for your accessories. At the factory, have that CA issue a certificate to each accessory as its produced, embedding identifying details, like the serial number, in that certificate. Then install that certificate and its associated private key (thus forming a digital identity) on the accessory as it comes off the line. Embed the CA’s certificate in your app. When you connect the accessory, verify that its certificate was issued by your CA. If it was, you can trust the identifying information, like the serial number, embedded in that certificate. With this setup your app will never connect to a malicious accessory. The worst that can happen is that you accidentally connect to the wrong instance of your accessory. IMPORTANT This is just an outline of one approach that I believe offers specific advantages. If you plan to deploy this in practice, hire a security profession to design your process. They can advise you on the details of creating a system that’s secure in practice. For example, they can help you: Create a mechanism that prevents your factory from leaking valid digital identities. Design your hardware to prevent an attacker from extracting an accessory’s digital identity from the accessory itself. To implement the URLSession side of this, change the shouldAllowHTTPSConnection(trust:) method like so: let waffleVarnisherCA: SecCertificate = { let u = Bundle.main.url(forResource: "WaffleVarnisherCA", withExtension: "cer")! let d = try! Data(contentsOf: u) let c = SecCertificateCreateWithData(nil, d as NSData)! return c }() func shouldAllowHTTPSConnection(trust: SecTrust) async -> Bool { var err = SecTrustSetPolicies(trust, SecPolicyCreateBasicX509()) guard err == errSecSuccess else { return false } err = SecTrustSetAnchorCertificates(trust, [self.waffleVarnisherCA] as NSArray) guard err == errSecSuccess else { return false } err = SecTrustSetAnchorCertificatesOnly(trust, true) guard err == errSecSuccess else { return false } let wasIssuedByOurCA = SecTrustEvaluateWithError(trust, nil) guard wasIssuedByOurCA else { return false } guard let chain = SecTrustCopyCertificateChain(trust) as? [SecCertificate], let trustedLeaf = chain.first else { return false } // C. Check the now-trusted leaf certificate return true } There are a few things to note about this code: It changes the trust policy (SecTrustSetPolicies) to the basic X.509 policy (SecPolicyCreateBasicX509) because the standard policy for TLS connections checks the DNS name, and that’s not appropriate for an accessory on the local network. It applies a custom anchor (SecTrustSetAnchorCertificates) and then disables all the other anchors (SecTrustSetAnchorCertificatesOnly) [1]. That prevents someone from impersonating your accessory by getting some other CA to issue a certificate that looks like the accessory’s certificate. It evaluates trust to confirm that the certificate was issued by the accessory’s CA. At point C you can trust the details in the accessory’s certificate (trustedLeaf). Here you might add code to confirm the identity of the accessory, to prevent you from accidentally connecting to the wrong accessory. [1] This is not strictly necessary because calling SecTrustSetAnchorCertificates disables other anchors. However, I’m applying both belt and braces [2] here. [2] I’m using the British idiom because belt and suspenders is so wrong (-:
Posted
by
Post not yet marked as solved
1 Replies
283 Views
For a GET request which is consistently the same, I'm returning in the response this response header. Cache-Control: max-age=3600, private I'm creating a URLSession with this configuration. Basically, ensuring my cache is large. The URLSession persists throughout the life of the app and all requests go through it.  var configuration = URLSessionConfiguration.default           let cache = URLCache(memoryCapacity: 500_000_000,                                diskCapacity: 1_000_000_000)         configuration.urlCache = cache         configuration.requestCachePolicy = .useProtocolCachePolicy I create a data task with the completion handler: let task = session.dataTask(with: request) { data, response, error in //... my response handling code here } task.resume() There isn't anything complicated or unusual I'm doing. I've used this code for years, and I'm now trying to optimise some calls with a caching policy. So I have 2 issues: Requests that have no-cache policy are still being saved into the URLCache. Which I've confirmed by looking into URLCache.shared.cachedResponse . Why is it caching them when the response explicitly states no-cache policy and I've told the session to use the protocol policy. On the request where I do want to use the cache, when I call session.dataTask on subsequent times, it never fetches from the cache. It always hits the server again - which I can confirm from the server logs. What piece of this am I missing here?
Posted
by
Post marked as solved
2 Replies
258 Views
Hi, I'm writing code to get a remote image via URL and I get the following error each time. I converted the URL to https as recommended. This is the error I get. URL is Optional(https://img.recipepuppy.com/627470.jpg) 2022-03-29 16:27:41.892870+1100 QantasTest[13489:314159] ATS failed system trust 2022-03-29 16:27:41.892949+1100 QantasTest[13489:314159] Connection 3: system TLS Trust evaluation failed(-9802) 2022-03-29 16:27:41.893119+1100 QantasTest[13489:314159] Connection 3: TLS Trust encountered error 3:-9802 2022-03-29 16:27:41.893212+1100 QantasTest[13489:314159] Connection 3: encountered error(3:-9802) 2022-03-29 16:27:41.894548+1100 QantasTest[13489:314159] Task <526F7B9B-ADC8-4D14-8EA6-DEAD629E7C5A>.<0> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9802]) 2022-03-29 16:27:41.894666+1100 QantasTest[13489:314182] NSURLConnection finished with error - code -1200 The code I use is: print("URL is \(String(describing: self.thumbNailUrl))") DispatchQueue.global().async { [weak self] in if let data = try? Data(contentsOf: self!.thumbNailUrl!) {             if let image = UIImage(data: data) {                                 DispatchQueue.main.async {                                     self?.imageView.image = image                                 }                             }                         }               } Any suggestions? Cheers Richard
Posted
by
Post not yet marked as solved
2 Replies
277 Views
I'm trying to create a network request mocking infrastructure for my iOS app. I'm using a mock URLProtocol to do this. My challenge is that I want to load custom data for each URLSession. In my setupWithError() in my unit tests, I basically do:         let config = URLSessionConfiguration.ephemeral         config.protocolClasses = [URLProtocolMock.self]         let session = URLSession(configuration: config)         APIManager.shared.session = session         <session.URLProtocolMock>.loadJSMocks(filename: "AddItemUITest.js") All of the examples I see for doing mocks this way, create static variables in the URLProtocolMock to allow configuring data. I'd like to be able to run tests in parallel, so static variables won't work. I couldn't find an obvious way to get the protocol instance from the URLSession. Is there a non-obvious way? ;-) Thanks! Greg P.S. And in the course of writing this, I realized that I have another problem with static APIManager session sharing. But that one I know how to solve :-)
Posted
by
Post marked as solved
2 Replies
347 Views
Hi team. Like the title, I'm figure out how to create my app so that I don't explicitly display local network privacy alerts. https://developer.apple.com/forums/thread/664116 This thread was a little helpful, but I couldn't find a clear way. Is there a way to automatically reject without explicitly displaying an alert, even if permission is required, such as plist settings? Thanks.
Posted
by