Writing secure mobile application code is difficult. The competing expectations of innovative user interfaces, new operating system features and API changes often leave security at the back of the list. Here at Codified Security we’ve created a mobile app security testing checklist for iOS to help you through the security testing process.
A high-level mobile app security testing checklist will help stop companies from being victims of the most critical and exploitable errors. These should be the first port of call for anyone concerned about mobile app security. Many of these recommendations contain links to more detailed articles and comprehensive checks.
1. SSL
A modern SSL implementation supporting TLS 1.2 is essential, especially given Apple’s App Transport Security requirements. Public networks are well known to be insecure and any good developer owes it to their users to encrypt their data and protect their privacy.
Many developers will ignore SSL certificates or hostname errors during development, which is easily forgot and renders SSL useless. To understand how to use SSL securely consider the checks a penetration testing would test for in this checklist. You should also consider implementing certificate key pinning to avoid Man in the Middle attacks.
2. Debug Code
The software architectures imposed by the iOS UI frameworks don’t lend themselves to easily-testable code, often making debug code and console logging the rule rather than the exception.
Many developers leave their debug code in production. This will still get compiled into the app along with any API calls that it contains. This makes it easy for your web and network infrastructure to be attacked. See the OWASP page on debug code.
For conditional compilation in Swift you need to ensure that your build settings have the DEBUG symbol set in your Swift compiler custom flags:
If this is the case you can then use preprocessor macros much like you would in Objective C or C++, though these are a little more constrained:
#if DEBUG // Add your debug code here #endif
3. Console Logging in Production Code
Leaving console logging in production code can be a huge security issue, as this can be read by anyone with access to the device. The worst example of this we’ve seen was a Fintech app that logged the details of every REST call. This included plain text passwords, CCVs, and addresses. This kind of error would be devastating if it fell into the wrong hands and can also cause PCI compliance issues. Ideally your application should use remote logging and not system logs.
By default the print() method in Swift will print to the console for both release and debug builds. To avoid this problem you can define the print() method in the global scope, overriding the one provided by Swift:
import Foundation func releasePrint(_ object: Any) { Swift.print(object) } func print(_ object: Any) { #if DEBUG Swift.print(object) #endif }
4. Web Views
Web views embedded within apps are often sandboxed properly by the operating system and frameworks. This is however web code, so the same rules (XSS, CSRF) apply. A cross-site scripting attack will allow session hijacking and access to the rest of the app.
Since iOS 8 developers have been encouraged to move away from UIWebView to the new WKWebView, with the main benefit being the introduction of the Nitro JavaScript engine. Another good reason to move is the introduction of the javaScriptEnabled property on the WKPreferences object. This property is true by default, but you should set it to false if the page you are rendering does not need to run JavaScript, preventing the possibility of cross-site scripting attacks.
5. App Files
Most apps will need to store persistent configuration information to ensure that the application maintains a consistent state between sessions. Some apps will also read configuration files from the filesystem, or even allow these files to be provided over the air by a back-end system.
In all of these cases data will be stored on the filesystem that could potentially be modified by an attacker. If this data is sensitive it might be more appropriate to move this configuration to the application binary if possible, or to ensure that the data is encrypted.
NSUserDefaults will store data unencrypted on the filesystem as a binary plist file inside your application’s folder. On a jailbroken device these files are easily accessible however, so you should never store sensitive information here without encrypting it first. A great alternative is SwiftKeychainWrapper, which allows you to use the iOS Keychain using almost the same API as the user defaults.
When using SQLite databases for sensitive data one option might be to use SQLCipher, which will encrypt the entire database. Though SQLCipher is a commercial product, there is an open source version available.
6. The Keychain
The iOS Keychain can be used for the secure storage of keys, passwords, certificates and other small items of data. Access to the data for other applications is controlled through Access Control Lists (ACLs) or Keychain Access Groups (for iCloud synchronized items).
The keychain handles encryption on behalf of the developer, encrypting the data before it’s stored on the filesystem. Unfortunately this encryption process is easily compromised on a jailbroken device, letting other applications read data from the app’s keychain.
Due to the known exploits above it is recommended that developers encrypt sensitive data before storing it in the keychain. If you’d like to learn more about iOS keychain security please check out the keychain section in our OWASP Top Ten article.
7. Data Protection
When storing data in standard files on disk you should ensure that the data is stored with the NSFileProtectionComplete flag set. This flag ensures that the data is encrypted at rest whenever the device is locked or booting.
When storing very sensitive data you should also make use of the iOS encryption facilities to ensure that your data is secure. The easiest way to implement encryption without using Keychain Services on iOS is to use the SecKeyfamily of functions, such as SecKeyEncrypt and SecKeyDecrypt. This will ensure that your data is secure, even if the Data Protection API is compromised. For more information check out the Security Framework reference.
8. Temporary Data
Data forensic tools can be used to recover deleted data quite easily. To avoid this issue you should ensure that sensitive temporary files are overwritten with random data before deleting them.
9. iTunes Backup
iTunes backups are stored encrypted, but these files have become easier to crack in recent years due to changes Apple made for iOS 10. If your application stores sensitive data that can easily be recovered through server communication or user interaction you should consider storing data in the Library/caches folder:
let fileURL = try! FileManager.default .url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false) .appendingPathComponent("data.dat")
10. Xcode
You should always keep Xcode up to date. Building the app with the most recent release will ensure that only the newest SSL ciphers are supported.
When updating Xcode you should also ensure that you’re downloading it from Apple directly, ideally via the Mac App Store. This ensures that you have the genuine version of Xcode from Apple as previous externally-hosted versions have been known to contain malware and unwittingly infect developers’ apps on the App Store.
11. Jailbroken Devices
To minimise attack vectors and potential loss of revenue you should stop the app from running on Jailbroken devices. There is a good tutorial here that explains some methods for detecting jailbroken devices. Whenever doing checks like this you should test thoroughly to ensure that none of your target devices or iOS versions trigger your detection code in error.
Another way to minimise attack vectors is to detect debuggers and prevent your app from running when a debugger is active. This Apple Q&A article explains the process along with some example code.
If this mobile app security testing checklist has got your attention and you want to know more about secure mobile development take at look this OWASP mobile app security checklist and these OWASP resources.
Codified Security is here to help make your mobile app secure whether it’s for iOS, Android, or to make sure you’re clearing the OWASP Mobile Top 10. For mobile app security testing try out Codified Security.