Skip to content

Latest commit

 

History

History
198 lines (136 loc) · 8.49 KB

9-security.md

File metadata and controls

198 lines (136 loc) · 8.49 KB

Security

Most users trust sensitive data to our applications and our goal is to protect that data.

iOS Secure Coding Practices Checklist

1. Minimize amount of 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.

2. Always encrypt sensitive 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.

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)
}

2.3. Encrypt stored data with a symmetric key

  • 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'")

3. Keep your connection secure

3.1. Do not disable App Transport Security

  • 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)

4. Make your auth flow seamless

4.1. Keep your access token lifetime reasonable

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

5. Do not store keys in the code

⚠️ ⚠️ ⚠️
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.

6. Validate custom URLs

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.

7. Care about your dependencies

7.1. Minimize permissions

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.

7.2. Monitor your dependencies

Monitor each change to your dependency manager lockfile. Even minor change can introduce major security risks.

Each dependency is your responsibility.

8. Avoid showing sensitive data on screenshots

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)
}

9. Do not log sensitive data

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: