Irbisa · Шпаргалка
Безопасность, приватность и Keychain
Классы защиты данных, ATS и TLS, pinning, биометрия, jailbreak, секреты в сборке, разрешения, ATT и геолокация
Что такое Keychain в iOS, как он защищён и какие элементы в нём можно хранить?
Можно ли спрятать API-ключ в
Info.plistили в.xcconfig? Где на самом деле должен жить секрет, от которого зависит приложение?Как добавить в приложение Face ID / Touch ID через
LocalAuthentication?import LocalAuthentication // Info.plist needs NSFaceIDUsageDescription, or Face ID requests failfunc unlockWithBiometrics() async -> Bool { let context = LAContext() context.localizedFallbackTitle = "Enter Password"…Как устроены runtime-разрешения в iOS: что будет, если нет
NSCameraUsageDescription, и что приложение может сделать после «Не разрешать»?import AVFoundationimport UIKit enum CameraAccess { case granted, denied, restricted } // Info.plist: NSCameraUsageDescription = "Scan the QR code on your parcel"…Как HTTPS защищает запрос: о чём договаривается TLS-рукопожатие и что приложение проверяет в сертификате сервера?
Какие есть классы Data Protection для файлов, какой файл получает по умолчанию и когда его стоит менять?
// A file that must be unreadable while the phone is lockedfunc saveStatement(_ pdf: Data, to url: URL) throws { try pdf.write(to: url, options: [.atomic, .completeFileProtection])} // On a directory: new files created inside inherit the class…Найдите баг: пользователей случайно разлогинивает, обычно когда приложение синхронизируется в фоне, а телефон заблокирован.
func saveToken(_ token: Data) { SecItemAdd([kSecClass: kSecClassGenericPassword, kSecAttrService: "auth", kSecValueData: token, kSecAttrAccessible: kSecAttrAccessibleWhenUnlocked] as CFDictionary, nil)}func readToken() -> String? { var result: CFTypeRef? let query = [kSecClass: kSecClassGenericPassword, kSecAttrService: "auth", kSecReturnData: true] as CFDictionary guard SecItemCopyMatching(query, &result) == errSecSuccess, let data = result as? Data else { return nil } return String(decoding: data, as: UTF8.self)}// Runs from a silent push and a BGAppRefreshTask, usually with the phone locked:func backgroundSync() async { guard let token = readToken() else { return session.logOut() } await api.sync(token: token)}Запрос по
httpsпадает с-1200и-9802в логе, хотя тот же URL открывается в браузере. Чего требует ATS и чего не хватает серверу, и как это проверить?Найдите баг: пиннинг в тесте работает, но прокси с установленным пользователем CA всё равно читает трафик приложения.
final class PinningDelegate: NSObject, URLSessionDelegate { let pinnedKeyHash: Data // SHA-256 of the server's public key init(pinnedKeyHash: Data) { self.pinnedKeyHash = pinnedKeyHash } func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping @Sendable (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard let trust = challenge.protectionSpace.serverTrust, SecTrustEvaluateWithError(trust, nil), let leaf = (SecTrustCopyCertificateChain(trust) as? [SecCertificate])?.first, let key = SecCertificateCopyKey(leaf), let raw = SecKeyCopyExternalRepresentation(key, nil) as Data?, Data(SHA256.hash(data: raw)) == pinnedKeyHash else { completionHandler(.performDefaultHandling, nil) // pin mismatch: use system trust return } completionHandler(.useCredential, URLCredential(trust: trust)) }}Как хешировать, шифровать и аутентифицировать данные с CryptoKit и где хранить ключ шифрования?
import CryptoKit // Hash: a fingerprint for integrity checks, not for storing passwordslet digest = SHA256.hash(data: Data("report.pdf contents".utf8))let hex = digest.map { String(format: "%02x", $0) }.joined()…Как устроена приватность в
Loggerиos_logи что никогда не должно попадать в лог устройства?import os let log = Logger(subsystem: "com.example.bank", category: "auth") func didSignIn(userID: Int, email: String, plan: String) { log.info("Signed in user \(userID)") // numbers: public by default…Как не допустить попадания чувствительных экранов в снапшот переключателя приложений и в запись экрана на iOS?
import SwiftUI struct BankRoot: View { @Environment(\.scenePhase) private var phase var body: some View {…Что такое App Tracking Transparency, когда можно показывать её запрос и что происходит с IDFA и атрибуцией рекламы, если пользователь отказал?
import AppTrackingTransparencyimport AdSupportimport UIKit // Info.plist: NSUserTrackingUsageDescription says what the user gets in return@MainActor…Чем доступ к геолокации When In Use отличается от Always и что меняется для вашего кода, если выключена «Точная геопозиция»?
import CoreLocation @MainActorfinal class LocationGate: NSObject, CLLocationManagerDelegate { private let manager = CLLocationManager()…Эта проверка на джейлбрейк стоит в банковском приложении. Что она ловит, как её обходят и что делать вместо
exit(0)?@MainActor func isJailbroken() -> Bool { let paths = ["/Applications/Cydia.app", "/bin/bash", "/usr/sbin/sshd", "/etc/apt"] if paths.contains(where: { FileManager.default.fileExists(atPath: $0) }) { return true } if UIApplication.shared.canOpenURL(URL(string: "cydia://")!) { return true } do { try "x".write(toFile: "/private/jb.txt", atomically: true, encoding: .utf8) return true // the sandbox should have refused this write } catch { return false }} // application(_:didFinishLaunchingWithOptions:)// if isJailbroken() { exit(0) }Что такое Secure Enclave и как создать и использовать в нём ключ через CryptoKit?
import CryptoKitimport Security enum EnclaveError: Error { case unavailable } func makeDeviceKey() throws -> SecureEnclave.P256.Signing.PrivateKey {…Что меняют
.userPresence,.biometryAnyи.biometryCurrentSetдля элемента Keychain и что с ним происходит, когда пользователь регистрирует новое лицо?import LocalAuthenticationimport Security func saveRefreshToken(_ token: Data) -> OSStatus { let access = SecAccessControlCreateWithFlags( nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .biometryCurrentSet, nil)!…Вы пинните публичный ключ сервера. Как сменить этот ключ, не отрезав пользователей на старых версиях приложения?
<key>NSAppTransportSecurity</key><dict> <key>NSPinnedDomains</key> <dict> <key>api.example.com</key> <dict>…Что такое DeviceCheck и App Attest и что каждый из них позволяет проверить вашему серверу?
import CryptoKitimport DeviceCheck enum AttestError: Error { case unsupported } // Once per install: create a key and let Apple vouch for it…Как бы вы спроектировали блокировку банковского приложения с 6-значным PIN, Face ID и автоблокировкой через минуту в фоне?