Most users trust sensitive data to our applications and our goal is to protect that data.
- 1. Minimize amount of data
- 2. Always encrypt sensitive data
- 3. Keep your connection secure
- 4. Make your auth flow seamless
- 5. Do not store keys in the code
- 6. Validate custom URLs
- 7. Care about your dependencies
- 8. Avoid showing sensitive data on screenshots
- 9. Do not log sensitive data
-
If the API responds with excessive responses containing a lot of unnecessary fields, talk to your backend team and ask them to return only required fields.
-
Don't store redundant data.
Whenever you want to store a single String
value or you would like to persist megabytes in CoreData you should never store it in plain text.
2.1. Use Keychain
Be careful with Keychain though. The items stored in the Keychain won't be removed when an application is uninstalled. The recommended approach is to wipe all Keychain data associated with an application when an app is first launched after installation.
2.2. Use iOS file encryption
On iOS 8 and above, files inside apps can be automatically encrypted using NSFileProtection
whenever the device is locked.
do {
try data.write(to: fileURL, options: .completeFileProtection)
}
- User Defaults
- Files
- Database cells
Instead of writing plain text let's encrypt it! For example using Themis:
let cellSeal = TSCellSeal(key: masterKeyData)!
let encryptedMessage = try! cellSeal.wrap("message".data(using: .utf8)!, context: nil)
userDefaults.set(encryptedMessage, forKey: "phoneNumber")
Or Realm built-in encryption:
// Generate a random encryption key
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { bytes in
SecRandomCopyBytes(kSecRandomDefault, 64, bytes)
}
// Open the encrypted Realm file
let realm = try! Realm(configuration: Realm.Configuration(encryptionKey: key))
// Use the Realm as normal
let dogs = realm.objects(Dog.self).filter("name contains 'Fido'")
- Do not allow insecure communication over HTTP.
- Do not disable ATS by setting
NSAllowsArbitraryLoads
. - If you really need to allow insecure communication with particular servers, use
NSExceptionDomains
:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>appanalytics.company.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
3.2. Use SSL pinning
Alamofire (and thus Moya) supports SSL pinning with a few lines of code:
let policies: [String: ServerTrustPolicy] = [
"test.example.com": ServerTrustPolicy.pinPublicKeys(
publicKeys: ServerTrustPolicy.publicKeys(),
validateCertificateChain: true,
validateHost: true
)
]
let sessionManager = SessionManager(
serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies)
)
let provider = MoyaProvider<AuthApi>(manager: sessionManager)
Make it shorter for applications which process a significant amount of sensitive data. For example, reasonable session length for a banking app is around 15 minutes.
4.2. Use Password AutoFill in all sign-up and sign-in flows
4.3. Whenever possible, support biometric authentication
4.4. Use ASWebAuthenticationSession for OAuth
⚠️ ⚠️ ⚠️
let apiKey = "f2801f1b9fd1"
let appId = "12312321"
Such keys can be easily parsed from the binary file by running strings MyApp
.
It's better to obfuscate keys and secrets like described here.
For applications that support deep links, it's recommended to thoroughly validate the URL and prompt user before triggering any important action.
Vulnerability Exploit Example
One example is the following bug in the Skype Mobile app, discovered in 2010: The Skype app registered the skype:// protocol handler, which allowed other apps to trigger calls to other Skype users and phone numbers. Unfortunately, Skype didn't ask users for permission before placing the calls, so any app could call arbitrary numbers without the user's knowledge.
Attackers exploited this vulnerability by putting an invisible <iframe src="skype://xxx?call"></iframe>
(where xxx was replaced by a premium number), so any Skype user who inadvertently visited a malicious website called the premium number.
Dependencies included in your application have access to the same data as your app does. This means that allowing access to the camera makes it possible for the 3rd party libraries to access both iPhone cameras any time the app is running. More on that in Felix's blog.
Monitor each change to your dependency manager lockfile. Even minor change can introduce major security risks.
Each dependency is your responsibility.
iOS has the concept of saving a screenshot when the application goes into the background. This feature can pose a security risk because screenshots (which may display sensitive information such as an email or corporate documents) are written to local storage, where they can be recovered by a rogue application with a sandbox bypass exploit or someone who steals the device.
To avoid such risks it's recommended to clean up user interface when an app goes to the background:
func applicationDidEnterBackground(_ application: UIApplication) {
let overlay = OverlayView()
window?.addSubview(overlay)
}
If an application is logging sensitive information, then its data will be captured on device logs. An attacker can easily dump device logs and retrieve the user's sensitive information.
Do not pass any sensitive information to the third party platforms like Mixpanel, Amplitude or Crashlytics.
Further Reading: