← Назад к вопросам

Где будешь хранить секретные картинки?

1.3 Junior🔥 121 комментариев
#Хранение данных

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Архитектура хранения защищенных изображений в iOS

Хранение секретных картинок (конфиденциальных изображений) требует многоуровневого подхода, сочетающего защиту на уровне файловой системы, шифрование и аппаратную безопасность. Вот комплексная стратегия:

1. Keychain Services (Связка ключей)

Для небольших изображений (например, миниатюр, QR-кодов) можно использовать Keychain с шифрованием AES. Хотя Keychain предназначен в первую очередь для текстовых данных, бинарные данные можно сериализовать.

import Security
import UIKit

class SecureImageStorage {
    static func saveImageToKeychain(image: UIImage, forKey key: String) throws {
        guard let imageData = image.pngData() else { throw KeychainError.conversionFailed }
        
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecValueData as String: imageData,
            kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
        ]
        
        SecItemDelete(query as CFDictionary)
        let status = SecItemAdd(query as CFDictionary, nil)
        guard status == errSecSuccess else { throw KeychainError.unhandledError(status: status) }
    }
}

2. Зашифрованное хранение в песочнице приложения

Для полноразмерных изображений оптимально использовать шифрование на лету при записи в директорию документах или кэша:

import CryptoKit

class EncryptedFileStorage {
    private let key: SymmetricKey
    
    init(key: SymmetricKey) {
        self.key = key
    }
    
    func saveImage(image: UIImage, fileName: String) throws {
        guard let imageData = image.jpegData(compressionQuality: 0.8) else { return }
        
        // Шифрование с использованием CryptoKit
        let sealedBox = try AES.GCM.seal(imageData, using: key)
        let fileURL = getDocumentsDirectory().appendingPathComponent(fileName)
        
        try sealedBox.combined?.write(to: fileURL, options: .completeFileProtectionUntilFirstUserAuthentication)
    }
    
    private func getDocumentsDirectory() -> URL {
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    }
}

3. Использование Data Protection API

iOS предоставляет встроенную защиту данных через атрибуты файлов:

let protectionAttributes: [FileAttributeKey: Any] = [
    .protectionKey: FileProtectionType.completeUntilFirstUserAuthentication
]

FileManager.default.createFile(
    atPath: filePath,
    contents: encryptedData,
    attributes: protectionAttributes
)

Доступные уровни защиты:

  • .complete – файл доступен только когда устройство разблокировано
  • .completeUnlessOpen – можно оставить открытым при блокировке
  • .completeUntilFirstUserAuthentication – защита снимается после первой разблокировки
  • .none – отсутствие шифрования (не использовать для секретных данных)

4. Keychain + Secure Enclave для хранения ключей

Самое надежное решение – комбинированный подход:

  1. Генерация криптографического ключа через Secure Enclave
  2. Сохранение ключа в Keychain с флагом kSecAttrAccessControl
  3. Шифрование изображения сгенерированным ключом
  4. Хранение зашифрованного файла в песочнице приложения
  5. Удаление временных расшифрованных данных из памяти
import LocalAuthentication

class BiometricProtectedStorage {
    func saveImageWithBiometricProtection(image: UIImage) throws {
        let context = LAContext()
        var error: NSError?
        
        guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            throw SecureStorageError.biometryNotAvailable
        }
        
        // Создаем защищенный доступ к ключу
        let accessControl = SecAccessControlCreateWithFlags(
            nil,
            kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
            .biometryCurrentSet,
            nil
        )
        
        // Генерируем и сохраняем ключ с биометрической защитой
        let key = SymmetricKey(size: .bits256)
        try saveKeyToKeychain(key: key, accessControl: accessControl!)
        
        // Шифруем и сохраняем изображение
        try encryptAndSaveImage(image: image, using: key)
    }
}

5. Дополнительные меры безопасности

  • Запрет на бэкапы – помечать файлы с атрибутом NSURLIsExcludedFromBackupKey
  • Очистка памяти – затирание буферов после использования
  • Время жизни – установка TTL (Time-To-Live) для конфиденциальных файлов
  • Детектирование джейлбрейка – отказ в работе на взломанных устройствах
  • Шифрование метаданных – защита не только содержимого, но и имен файлов

Рекомендации по выбору стратегии

КритерийРекомендуемое решение
Небольшие изображения (< 100KB)Keychain с атрибутом kSecAttrAccessibleWhenUnlockedThisDeviceOnly
Средние изображения (100KB - 10MB)Шифрование AES + Data Protection API
Критически важные данныеSecure Enclave + биометрическая аутентификация
Временные секретные данныеKeychain + автоматическое удаление по таймеру

Главный принцип: никогда не храните секретные изображения в открытом виде в файловой системе, даже временно. Используйте сквозное шифрование от момента получения данных до их отображения на экране, с минимальным временем нахождения в оперативной памяти в расшифрованном виде. Для максимальной безопасности комбинируйте несколько уровней защиты: аппаратный Secure Enclave для ключей, Keychain для их хранения, Data Protection для файлов и биометрию для аутентификации пользователя.