Shikhar Varshney

Best practices to avoid security vulnerabilities in your iOS App

Every program is a potential target for hackers. They would want to tear you down and make you kneel. So, what do we do? I think we should stop writing programs and put our laptop lids down?

Naah…. Just kidding!!

Attackers will try to find security vulnerabilities in your application. They will then try to use these vulnerabilities to steal secrets, corrupt programs and data. Your customers’ private information and your reputation are at stake.

Security is not something that can be added to software as an afterthought; just as a shed made out of cardboard cannot be made secure by adding a padlock to the door, an insecure tool or application may require extensive redesign to secure it. You must identify the nature of the threats to your app and incorporate secure coding practices throughout the planning and development of your product.

security

Secure coding is the practice of writing programs that are resistant to attack by malicious or mischievous people or programs. Secure coding helps protect a user’s data from theft or corruption.

Most software security vulnerabilities fall into one of these small set of categories:

  • Buffer overflows
  • Unvalidated input
  • Race conditions
  • Access-control problems
  • Weaknesses in authentication, authorization, or cryptographic practices

I am not going to bore you with the theory of each type of vulnerability here. Duhh!! Who does that nowadays??

Instead, I am going to share a few examples from my own experience which I came across while going through an enterprise based security scan of my code.

Observation 1- Buffer Overflow

Abstract- The program writes outside the bounds of allocated memory, which could corrupt data, crash the program, or lead to the execution of malicious code.

Explanation-  Let’s have a look at this piece of code

void GPBSetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber,
                   BOOL value) {
  if (idx < 0) {
    NSCAssert(fieldNumber != 0, @"Invalid field number.");
    uint32_t *has_storage = self->messageStorage_->_has_storage_;
    has_storage[-idx] = (value ? fieldNumber : 0);
  } else {
    NSCAssert(idx != GPBNoHasBit, @"Invalid has bit.");
    uint32_t *has_storage = self->messageStorage_->_has_storage_;
    uint32_t byte = idx / 32;
    uint32_t bitMask = (1 << (idx % 32));
    if (value) {
      has_storage[byte] |= bitMask;
    } else {
      has_storage[byte] &= ~bitMask;
    }
  }
}

As you can see in line 2 of the method, variable ‘has_storage’ has been declared as an unsigned 32 bit integer and assigned a value. However in line 3, a value is assigned to some index value of it. This is the classic example of possibility of Buffer overflow.

This code snippet is a part of Google’s Firebase/Messaging pods framework.

Fix

Avoid declaring the variables by keeping such vulnerabilities in mind i.e you can define this as:-

uint32_t  _has_storage_[0];

Observation 2- Privacy Violation: HTTP GET

Abstract- The identified call uses the HTTP GET instead of POST method to send data to the server.

Explanation- HTTP requests which utilize the GET method allow the URL and request parameters to be cached in the browser’s URL cache, intermediary proxies, and server logs. This could expose sensitive information to individuals who do not have appropriate rights to the data.

Example 1: The following code makes an HTTP request using the GET HTTP method instead of POST.


let url = URL(string: “https://www.somesvr.com/someapp/user”)
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = “GET”
let connection = NSURLConnection(request:request, delegate:self)

Example 2: If the application uses NSURLRequest then the default HTTP method is GET.

let url = URL(string: “https://www.somesvr.com/someapp/user”)
let request = URLRequest(URL: url!)
let connection = NSURLConnection(request:request, delegate:self)

Since most of us are not aware that while making a URLRequest in Swift, if we do not provide any HTTP method then the default method is “GET” which can be treated as a major vulnerability in many of the Static Code Analyzers.

Fix

Make an extension of the URLRequest class and add a method with some added parameters as per your convenience.

extension URLRequest {
    /// Creates an instance with the specified `method`, `urlString` and `headers`.
    ///
    /// - parameter url:     The URL.
    /// - parameter method:  The HTTP method.
    /// - parameter headers: The HTTP headers. `nil` by default.
    ///
    /// - returns: The new `URLRequest` instance.
    public init(url: URLConvertible, method: HTTPMethod, headers: HTTPHeaders? = nil)
    throws {
        let url = try url.asURL()

        self.init(url: url)

        httpMethod = method.rawValue

        if let headers = headers {
            for (headerField, headerValue) in headers {
                setValue(headerValue, forHTTPHeaderField: headerField)
            }
        }
    }

    func adapt(using adapter: RequestAdapter?) throws -> URLRequest {
        guard let adapter = adapter else { return self }
        return try adapter.adapt(self)
    }
}

 

func adapt (using adapter: RequestAdapter?) throws -> URLRequest {

       guard let adapter = adapter else { return self }
       return try adapter.adapt(self)
}

And then make your URLRequest in your Router file as follows-

var urlRequest = try URLRequest(url: URL, method: route.method)

This will put an end to all your misery and will make your beloved code more secure.

Observation 3- Insecure Storage: HTTP Response Cache Leak

Abstract- The identified method performs a URL request without configuring the URL loading system to prevent the caching of HTTP(S) responses.

Explanation- The HTTP(S) responses may contain sensitive data such as session cookies and API tokens. The URL loading system will cache all the HTTP(S) responses for performance reasons, storing them unencrypted in the {app ID}/Library/Caches/com.mycompany.myapp/Cache.db* files. Developers may think that by setting the diskCapacity or memoryCapacity properties of the URLCache class to 0, they may be effectively disabling the HTTP(S) response cache system. However, the NSURLCache documentation states that both the on-disk and in-memory caches will be truncated to the configured sizes only if the device runs low on memory or disk space. Both settings are meant to be used by the system to free system resources and improve performance, not as a security control.

Fix

The combination of two solutions works best for plumbing these types of leaks. Firstly, after the response has been received, remove all the cache that has been saved to the memory by using this small snippet

URLCache.shared.removeAllCachedResponses()

And secondly, you can use this delegate method in your session manager class-

open func urlSession(
        _ session: URLSession,
        dataTask: URLSessionDataTask,
        willCacheResponse proposedResponse: CachedURLResponse,
        completionHandler: @escaping (CachedURLResponse?) -> Void)
    {
        
        var newProposedResponse:CachedURLResponse? = proposedResponse
        if newProposedResponse?.response.url?.scheme == "https" {
            
            newProposedResponse = nil
            completionHandler(newProposedResponse)
        }
}

This just checks whether a cached response has been sent and, if it finds one, sets it to nil. You can also conditionally cache data by examining the properties of the response before returning the object to cache.

Note– You may also alter the configuration policy of your URLRequest to ignore storing any Cache by using this code snippet while making your request-

configuration.requestCachePolicy = .reloadIgnoringCacheData

 

Observation 4- Insecure Transport: Weak SSL Protocol

Abstract- The SSLv2, SSLv23, and SSLv3 protocols contain several flaws that make them insecure, so they should not be used to transmit sensitive data.

Explanation- The Transport Layer Security (TLS) and Secure Sockets Layer (SSL) protocols provide a protection mechanism to ensure the authenticity, confidentiality and integrity of data transmitted between a client and web server. Both TLS and SSL have undergone revisions resulting in periodic version updates. Each new revision was designed to address the security weaknesses discovered in the previous versions. Use of an insecure version of TLS/SSL will weaken the strength of the data protection and could allow an attacker to compromise, steal, or modify sensitive information.

Weak versions of TLS/SSL may exhibit one or more of the following properties:

– No protection against man-in-the-middle attacks
– Same key used for authentication and encryption
– Weak message authentication control
– No protection against TCP connection closing

The presence of these properties may allow an attacker to intercept, modify, or tamper with sensitive data.

Example 1: The following example configures the session to use SSL v3.0:

let configuration : NSURLSessionConfiguration = 
NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.TLSMinimumSupportedProtocol = kSSLProtocol3
let mySession = NSURLSession(configuration: configuration, delegate: self, 
delegateQueue: operationQueue)

 

Fix

In most of the networking libraries that we use in iOS like Alamofire and AFNetworking, the default setting is to use SSL Protocol and hence if we explicitly update the minimum supported protocol in our code to the latest TLS protocol version, then we can easily prevent this vulnerability in our code. This can be done as follows-

let safeConfiguration = URLSessionConfiguration.default
safeConfiguration.tlsMinimumSupportedProtocol = .tlsProtocol12

 

Observation 5- Input Interception: Keyboard Extensions Allowed 

Abstract- The application allows third party keyboard extensions to be allowed.

Explanation- Keyboard extensions are allowed to read every single keystroke that a user enters. Third-party keyboards are normally used to ease the text input or to add additional emoticons and they may log what the user enters or even send it to a remote server for processing. Malicious keyboards can also be distributed to act as a key-logger and read every key entered by the user in order to steal sensitive data such as credentials or credit card numbers.

Fix

If you want that no third party keyboard can be installed while using your application, then add this code snippet into your AppDelegate.swift file.

func application(_ application: UIApplication, 
shouldAllowExtensionPointIdentifier extensionPointIdentifier: 
UIApplicationExtensionPointIdentifier) -> Bool {
    if extensionPointIdentifier == .keyboard {
                   
        return false
    
    }
        
    return true
}

This method will prevent your app to install any keyboard apart from the one that is provided by iOS.

Observation 6- Insecure Storage: Lacking Data Protection

Abstract-  The identified method writes data to a file lacking sufficient encryption settings.

Explanation- Even though all files on an iOS device, including those without an explicitly assigned Data Protection class, are stored in an encrypted form; we can specify NSFileProtectionNone which results in encryption using a key derived solely based on the device’s UID. This leaves such files accessible any time the device is powered on, including when locked with a passcode or when booting. As such, usages of NSFileProtectionNone should be carefully reviewed to determine if further protection with a stricter Data Protection class is warranted.

In the following example, the given data is not protected (accessible anytime the device is powered on):

...
let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains
(.DocumentDirectory, .UserDomainMask, true)[0])
let filename = "\(documentsPath)/tmp_activeTrans.txt"
...
BOOL ok = textData.writeToFile(filepath, options: .DataWritingFileProtectionNone);
...

 

Fix

-NSFileProtectionComplete, NSDataWritingOptions.DataWritingFileProtectionComplete:
The resource is stored in an encrypted format on disk and cannot be read from, or written to, while the device is locked or booting. It’s available in iOS 4.0 and later.

-NSFileProtectionCompleteUnlessOpen, NSDataWritingOptions.DataWritingFileProtectionCompleteUnlessOpen:
The resource is stored in an encrypted format on disk. Resources can be created while the device is locked, but once closed, cannot be opened again until the device is unlocked. If the resource is opened when unlocked, you may continue to access the resource normally, even if the user locks the device.
Available in iOS 5.0 and later.

-NSFileProtectionCompleteUntilFirstUserAuthentication, NSDataWritingOptions.DataWritingFileProtectionCompleteUntilFirstUserAuthentication:
The resource is stored in an encrypted format on disk and cannot be accessed until after the device has booted. After the user unlocks the device for the first time, your app can access the resource and continue to access it even if the user subsequently locks the device.
Available in iOS 5.0 and later.

-NSFileProtectionNone, NSDataWritingOptions.DataWritingFileProtectionNone:
The resource has no special protections associated with it. It can be read from, or written to, at any time.
Available in iOS 4.0 and later.

Oh!! My God… So many observations. Who writes such a vulnerable code anyway??

Me, you??

Let me tell you something folks! Privacy and Security are two important constructs of today’s digital umbrella which covers a huge part of our society. And moving forward we are going to be more dependent on all these digital devices lying around us exploiting the technologies like AR, AI, IoT etc. Did I just sound like Mr. Snowden?? Believe me, I am “No One”(pun intended).

But, it’s the least, we as developers can do to make our code less prone, a little bit more secure by keeping in mind certain techniques while coding. After all, good code is contagious. It spreads.

Thanks for reading guys 🙂

References-

  1. https://www.apple.com/business/docs/iOS_Security_Guide.pdf
  2. https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StrategiesforImplementingYourApp/StrategiesforImplementingYourApp.html#//apple_ref/doc/uid/TP40007072-CH5-SW21
  3. David Thiel iOS Application Security: The Definitive Guide for Hackers and Developers No Starch Press
  4. https://developer.apple.com/library/content/documentation/Security/Conceptual/SecureCodingGuide/Articles/TypesSecVuln.html
 

Related Articles

#Tech

NHibernate, Linq and Lazy Collections

For the past few months we have been busy building a services framework for one of our clients so that they can build all of their enterprise web services without ever having to worry about the cross cutting concerns and... Read more
#Tech

Page Redirects using Spring.NET

Who is responsible for page redirects in ASPNET MVP – The View or the Presenter None of the above, it is you :) On a serious note, it is the View as one shouldn’t pollute the Presenter with the page navigation... Read more