Confidential data leak when caching network requests on the iOS platform
In the context of developing applications for mobile platforms, caching of internetwork requests can be classified as recommended and most frequently used practices, since it allows developers to significantly improve application performance. Development kits for modern mobile platforms usually include packages that automate the process of processing and caching internetwork requests, in particular HTTP requests.
Like caching dynamic memory requests or conditional jumps in microprocessors, the interfaces of classes that cache HTTP requests are transparent to the developer and perform caching in the background. Continuing the analogy with the caching functional block in the processor, it can be noted that the lack of understanding of the internal structure of the caching mechanism often leads to gross programming errors. In the context of mobile applications, improper use of the HTTP request cache leads to a decrease in application performance and a partial or complete loss of privacy. An example of a common error leading to performance degradation is application caching (Double caching).
Battle test
Consider an example of misuse of the Internet request cache. As a result of the study of Appendix B in the context of the security analysis, it was found that the application caches HTTP requests made as part of interaction with the main web service. Internet requests are stored in the local SQLite database in the application’s home directory at the relative address Library / Caches /
CREATE TABLE cfurl_cache_schema_version(schema_version INTEGER);
CREATE TABLE cfurl_cache_response(entry_ID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, version INTEGER, hash_value INTEGER, storage_policy INTEGER, request_key TEXT UNIQUE, time_stamp NOT NULL DEFAULT CURRENT_TIMESTAMP);
CREATE TABLE cfurl_cache_blob_data(entry_ID INTEGER PRIMARY KEY, response_object BLOB, request_object BLOB, proto_props BLOB, user_info BLOB);
CREATE TABLE cfurl_cache_receiver_data(entry_ID INTEGER PRIMARY KEY, receiver_data BLOB);
CREATE INDEX request_key_index ON cfurl_cache_response(request_key);
CREATE INDEX time_stamp_index ON cfurl_cache_response(time_stamp);
CREATE INDEX proto_props_index ON cfurl_cache_blob_data(entry_ID);
CREATE INDEX receiver_data_index ON cfurl_cache_receiver_data(entry_ID);
The request_key column of the cfurl_cache_response database table stores the Uniform Resource Identifier (URI) that the application requested access to, including HTTPS requests. Such requests may include user confidential data, including user authentication data. For example, application B uses a quasi-RESTful interface to interact with a web service and performs a GET request to authorize the user.
sqlite> select request_key from cfurl_cache_response;
...
https://example.com/api/1.0/auth?login=Selma_Nagel&password=qwerty
...
The receiver_data column of the cfurl_cache_receiver_data database table stores the server responses that were received by the application, including requests over the HTTPS protocol. So, in Appendix B, a web form of payment for services using a bank card is used.
sqlite> select receiver_data from cfurl_cache_receiver_data;
...
...
Thus, confidential user data is stored on the storage device of the mobile device in unencrypted form. There can be several vectors of attack on a vulnerable application: from virus software running on a user's PC to malicious applications targeted at jailbroken devices. You can read more about this, as well as typical errors in storing confidential data in our previous article .
How was this possible?
An object of the NSURLCache class, accessible via the + sharedURLCache method, is responsible for caching internetwork requests within the framework of a mobile application carried out by objects of the NSURLConnection class. Objects of the NSURLCache class contain two parameters that control the volume of the container in dynamic memory (In-Memory Cache) and on the storage device (On-Disk Cache). Read more about the NSURLCache class in the official documentation .
On iOS v4.x and below operating systems, there was no caching of Internet requests on the storage device, and the parameter of the diskCapacity class was ignored. Starting with iOS versions 5 and above, the application by default creates an HTTP request cache on the hard drive of a mobile device, limited to a capacity of 20 MB. And starting with the 6th version of iOS, support for caching requests via the HTTPS protocol is implemented.
It seems worthwhile to mention that all mobile applications for the iOS platform that explicitly or implicitly perform interworking requests using NSURLConnection are vulnerable. In particular, the firewall classes of the popular AFNetworking library are wrappers over NSURLConnection. Therefore, mobile applications using AFNetworking are also potentially vulnerable.
How to protect the application?
The easiest method to protect against caching of requests that include sensitive information is to set the diskCapacity of the sharedURLCache object to 0.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[[NSURLCache sharedURLCache] setDiskCapacity:0];
...
}
With the use of delegates, this can be done like this.
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
/* Implement cacheing logic here. */
return nil; // HACK: just do not cache
}
Fine tune with Cache-Control
The solutions mentioned above are unlikely to be called elegant (most likely the other way around). They are suitable for quickly correcting the logic of the application, however, to fine-tune the interaction of the client application with the web service, we can resort to changes in the policy for caching requests on the server side. The cache policy is set in the Cache-Control HTTP header ( RFC 2616 ).
To demonstrate the behavior of NSURLCache depending on the policy of caching web resources, we have compiled a simple application for the iOS mobile platform. The iOS SDK v7.0 package was used, and the application was launched on the device simulator.

Fig. 1. The work of the test application
The results of our little research in are given in table 1. Note that contrary to popular belief, the application caches Internet requests even without the Cache-Control header value set.
| Resource | Cache control | Cached |
|---|---|---|
| / htbin / mr | must-revalidate | Yes |
| / htbin / nocache | no-cache | Yes |
| / htbin / nostore | no-store | Not |
| / htbin / private | private | Yes |
| / htbin / public | public | Yes |
| / htbin / woheader | Not specified | Yes |
| / htbin / implicit | max-age = 604800 | Yes |
Tab. 1. Caching of requests on disk depending on Cache-Control
Thus, the most reasonable solution aimed at eliminating the identified vulnerability is to set the Cache-Control value to no-store for requests containing confidential information. Given the semantics of the no-store value, it is safe to say that most client applications will not cache such requests.
no-store
The purpose of the no-store directive is to prevent the inadvertent release or retention of sensitive information (for example, on backup tapes). The no-store directive applies to the entire message, and MAY be sent either in a response or in a request. If sent in a request, a cache MUST NOT store any part of either this request or any response to it. If sent in a response, a cache MUST NOT store any part of either this response or the request that elicited it. This directive applies to both non-shared and shared caches. "MUST NOT store" in this context means that the cache MUST NOT intentionally store the information in non-volatile storage, and MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible after forwarding it.
Even when this directive is associated with a response, users might explicitly store such a response outside of the caching system (eg, with a "Save As" dialog). History buffers MAY store such responses as part of their normal operation.
The purpose of this directive is to meet the stated requirements of certain users and service authors who are concerned about accidental releases of information via unanticipated accesses to cache data structures. While the use of this directive might improve privacy in some cases, we caution that it is NOT in any way a reliable or sufficient mechanism for ensuring privacy. In particular, malicious or compromised caches might not recognize or obey this directive, and communications networks might be vulnerable to eavesdropping.
Conclusion
In this article, it was shown that starting with version 5 of the iOS operating system, when using the NSURLConnection class with default parameters, caching of web requests to disk is allowed. In particular, confidential data, such as logins, passwords, credit card numbers, etc., may appear in the disk cache. In other words, confidential data is stored on disk without the knowledge of the application or user. Almost all applications that interact over the network and transmit sensitive data are potentially vulnerable.
Malicious applications running on personal computers can extract data from the disk cache when connecting an iOS device to a personal computer. This can be a trojan on the victim’s computer, or a special program on the attacker's computer, provided that the latter receives physical access to the device for a short time.
At the application level, this potential vulnerability is closed by explicitly prohibiting local caching when creating NSURLConnection. At the server level, this potential vulnerability should be closed by indicating the prohibition of caching in the HTTP headers of server responses for all requests containing confidential data.