Non-encrypted confidential information - Local data - Swift

Non-encrypted confidential information - Local data - Swift

Need

Secure storage of confidential information on local devices

Context

  • Usage of Swift for iOS and macOS app development
  • Usage of Foundation for building responsive and customizable web interfaces
  • Usage of Firebase for building scalable and real-time web and mobile applications

Description

Non compliant code

        import Foundation
import Firebase

func storeDataLocally() {
    // Fetch data from Firebase
    let db = Firestore.firestore()
    db.collection("users").getDocuments { (snapshot, error) in
        if let error = error {
            print("Error getting documents: \\(error)")
        } else {
            for document in snapshot!.documents {
                print("\\(document.documentID) => \\(document.data())")
                
                // Store data locally in UserDefaults
                UserDefaults.standard.set(document.data(), forKey: document.documentID)
            }
        }
    }
}
        
        

The above Swift code fetches data from a Firebase Firestore database and stores it locally on the device using UserDefaults.

The vulnerability lies in the fact that the data is stored in plain text. UserDefaults is not a secure place to store sensitive information because it is not encrypted. The data stored in UserDefaults is easily accessible to anyone who has access to the device and can bypass the device's security mechanisms.

In this case, confidential Firebase data is being stored in plain text on the device's local storage. This means that if the device is lost or stolen, an attacker could potentially access this data without needing to decrypt it. This represents a significant security risk, especially if the data being stored is sensitive or confidential.

The best practice is to encrypt sensitive data before storing it locally or to use secure storage mechanisms provided by the operating system, which automatically encrypt the data.

Steps

  • Use encryption to protect the confidential data stored locally on the device.
  • Avoid storing sensitive data in plain text format.
  • Implement secure storage mechanisms such as Keychain or encrypted databases to store sensitive data.
  • Ensure that the data is encrypted before storing it locally.
  • Use secure communication protocols (e.g., HTTPS) when fetching data from Firebase.
  • Implement proper access controls and authentication mechanisms to restrict unauthorized access to the data.
  • Consider implementing remote wipe or data encryption features to protect data in case of device theft or loss.

Compliant code

        import Foundation
import Firebase
import Security
import KeychainSwift

func storeDataLocally() {
    // Fetch data from Firebase
    let db = Firestore.firestore()
    let keychain = KeychainSwift()
    
    db.collection("users").getDocuments { (snapshot, error) in
        if let error = error {
            print("Error getting documents: \\(error)")
        } else {
            for document in snapshot!.documents {
                print("\\(document.documentID) => \\(document.data())")
                
                // Encrypt data before storing
                if let jsonData = try? JSONSerialization.data(withJSONObject: document.data(), options: []),
                   let jsonString = String(data: jsonData, encoding: .utf8),
                   let data = jsonString.data(using: .utf8) {
                    
                    // Store data securely in Keychain
                    keychain.set(data, forKey: document.documentID)
                }
            }
        }
    }
}
        
        

The updated code now uses the KeychainSwift library to securely store the data fetched from Firebase. KeychainSwift is a wrapper for the iOS Keychain that is used to secure sensitive data.

First, we import the necessary libraries at the top of the file. We then initialize a KeychainSwift object within the storeDataLocally function.

When we fetch the data from Firebase, we first convert the data to JSON and then to a string. This string is then converted to Data, which can be securely stored in the Keychain.

The keychain.set(data, forKey: document.documentID) function call stores the data in the Keychain. The document.documentID is used as the key for the Keychain entry.

This way, the data is encrypted before it is stored locally, and it is stored in a secure storage mechanism (Keychain), which protects it from unauthorized access. This mitigates the risk of an attacker being able to view the data in plain text if they gain access to the physical device.

References