Architecture Securite
Protection de niveau bancaire pour toutes les applications Apple YaniPay. Secure Enclave, CryptoKit, biometrie et chiffrement AES-256-GCM assurent la securite de bout en bout de vos donnees financieres.
Overview
L'architecture de securite YaniPay repose sur une approche defense-in-depth, exploitant les fonctionnalites de securite materielle d'Apple au maximum. Chaque couche ajoute une protection supplementaire, de la puce Secure Enclave jusqu'au masquage des donnees dans l'interface utilisateur.
Notre implementation combine le Secure Enclave pour le stockage materiel des cles, CryptoKitpour le chiffrement AES-256-GCM, et l'authentification biometrique (Face ID / Touch ID) pour offrir un niveau de protection bancaire certifie.
Defense en Profondeur
L'architecture de securite YaniPay utilise 6 couches de protectionindependantes. Meme si une couche est compromise, les autres continuent de proteger les donnees de l'utilisateur. Cette approche est conforme aux standards PCI DSS et aux recommandations de l'ANSSI.
Authentification Biometrique
YaniPay integre Face ID et Touch ID pour une authentification sans friction. Le BiometricAuthManagergere automatiquement la detection du type de capteur disponible et propose un fallback vers le code d'acces en cas d'echec biometrique.
import LocalAuthentication
class BiometricAuthManager: ObservableObject {
@Published var isAuthenticated = false
@Published var biometricType: LABiometryType = .none
func authenticate() async throws -> Bool {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
error: &error
) else {
throw AuthError.biometricNotAvailable
}
// Store biometric type for UI adaptation
biometricType = context.biometryType
let success = try await context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Authentifiez-vous pour acceder a YaniPay"
)
await MainActor.run {
isAuthenticated = success
}
return success
}
func checkBiometricAvailability() -> BiometricStatus {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
error: &error
) {
return .available(context.biometryType)
}
return .unavailable(error?.localizedDescription ?? "Unknown")
}
}Adaptation UI Automatique
Le BiometricAuthManagerdetecte automatiquement le type de capteur (Face ID sur iPhone X+, Touch ID sur les anciens modeles) et adapte les icones et le texte de l'interface en consequence.
Chiffrement CryptoKit
Toutes les donnees sensibles sont chiffrees avec AES-256-GCM via le framework CryptoKit d'Apple. Les cles de chiffrement sont generees et stockees dans le Secure Enclave, garantissant qu'elles ne sont jamais exposees en memoire principale.
import CryptoKit
import Security
class EncryptionManager {
// MARK: - AES-256-GCM Encryption
/// Encrypt data using AES-256-GCM
func encrypt(_ data: Data, key: SymmetricKey) throws -> Data {
let sealedBox = try AES.GCM.seal(data, using: key)
guard let combined = sealedBox.combined else {
throw EncryptionError.sealingFailed
}
return combined
}
/// Decrypt data using AES-256-GCM
func decrypt(_ data: Data, key: SymmetricKey) throws -> Data {
let sealedBox = try AES.GCM.SealedBox(combined: data)
return try AES.GCM.open(sealedBox, using: key)
}
// MARK: - Secure Enclave Key Generation
/// Generate an ECC P-256 key pair stored in Secure Enclave
func generateSecureKey() throws -> SecKey {
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "com.yanipay.secure-key",
kSecAttrAccessControl as String: try createAccessControl()
]
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(
attributes as CFDictionary, &error
) else {
throw EncryptionError.keyGenerationFailed(
error?.takeRetainedValue().localizedDescription ?? "Unknown"
)
}
return privateKey
}
// MARK: - Access Control
private func createAccessControl() throws -> SecAccessControl {
var error: Unmanaged<CFError>?
guard let access = SecAccessControlCreateWithFlags(
kCFAllocatorDefault,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.privateKeyUsage, .biometryCurrentSet],
&error
) else {
throw EncryptionError.accessControlFailed
}
return access
}
}Secure Enclave
Le Secure Enclave est un coprocesseur cryptographique isole present sur les puces Apple A7+ et M1+. Les cles privees generees dans le Secure Enclave ne peuvent jamaisetre extraites, meme par le systeme d'exploitation.
Couches de Securite
L'architecture de securite YaniPay est composee de 6 couches independantes, chacune renforçant la precedente pour une protection maximale.
Secure Enclave
Stockage materiel des cles cryptographiques isole du processeur principal. Les cles privees ne quittent jamais le coprocesseur securise.
AES-256-GCM
Chiffrement de grade militaire avec authentification integree. Protege toutes les donnees sensibles en transit et au repos.
Certificate Pinning
Verification stricte des certificats SSL/TLS pour empecher les attaques Man-in-the-Middle sur les communications API.
Jailbreak Detection
Detection multi-couche des appareils compromis : fichiers suspects, permissions d'ecriture anormales et environnement d'execution.
Data Masking
Masquage automatique des donnees sensibles dans l'UI et les logs : cartes (**** 0366), emails (u***r@...), IBAN (FR** **** 1234).
Keychain Services
Stockage securise des identifiants, tokens et secrets dans le Keychain iOS chiffre avec protection d'acces biometrique.
Detection Jailbreak
YaniPay integre un systeme de detection de jailbreak multi-couche. L'application refuse de fonctionner sur un appareil compromis pour proteger les donnees financieres de l'utilisateur. La detection s'effectue au demarrage et periodiquement pendant l'utilisation.
import Foundation
import UIKit
class EnhancedSecurityManager {
/// Multi-layer jailbreak detection
func isDeviceCompromised() -> Bool {
// Layer 1: Check for suspicious files
let suspiciousPaths = [
"/Applications/Cydia.app",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/usr/sbin/sshd",
"/usr/bin/apt",
"/bin/bash",
"/usr/lib/system/libsystem_kernel.dylib",
"/var/cache/apt",
"/var/lib/apt",
"/var/lib/cydia",
"/var/log/syslog",
"/private/var/stash"
]
for path in suspiciousPaths {
if FileManager.default.fileExists(atPath: path) {
return true
}
}
// Layer 2: Write test (jailbroken devices allow writes
// to restricted directories)
let testPath = "/private/jailbreak_test"
do {
try "test".write(
toFile: testPath,
atomically: true,
encoding: .utf8
)
try FileManager.default.removeItem(atPath: testPath)
return true // Write succeeded = compromised
} catch {
// Expected: write should fail on non-jailbroken device
}
// Layer 3: Check URL schemes
if let url = URL(string: "cydia://package/com.example") {
if UIApplication.shared.canOpenURL(url) {
return true
}
}
// Layer 4: Check for dynamic libraries
let dylibs = [
"SubstrateLoader.dylib",
"SSLKillSwitch2.dylib",
"0Shadow.dylib"
]
for dylib in dylibs {
if let _ = dlopen(dylib, RTLD_NOW) {
return true
}
}
return false
}
/// Action when compromised device detected
func handleCompromisedDevice() {
// Wipe sensitive data from Keychain
KeychainManager.shared.deleteAll()
// Notify backend
SecurityReporter.shared.reportCompromise()
// Show alert and block app usage
DispatchQueue.main.async {
NotificationCenter.default.post(
name: .deviceCompromised,
object: nil
)
}
}
}Securite Critique
Lorsqu'un appareil jailbreake est detecte, YaniPay supprime immediatement toutes les donnees sensibles du Keychain, notifie le backend et bloque l'acces a l'application. Cette mesure protege les fonds de l'utilisateur contre les malwares potentiels.
Masquage des Donnees
YaniPay masque automatiquement toutes les donnees sensibles dans l'interface utilisateur et dans les logs applicatifs. Le DataMaskingManagerapplique des regles de masquage specifiques a chaque type de donnee.
| Type | Donnee originale | Donnee masquee |
|---|---|---|
| Carte bancaire | 4976 1234 5678 0366 | **** **** **** 0366 |
| user@yanipay.com | u***r@yanipay.com | |
| IBAN | FR76 3000 4028 3700 0100 0366 | FR** **** **** **** **** 0366 |
| Telephone | +33 6 12 34 56 78 | +33 6 ** ** ** 78 |
| Nom | Johan Lepinay | J***n L***y |
class DataMaskingManager {
static let shared = DataMaskingManager()
/// Mask credit card number: **** **** **** 0366
func maskCard(_ number: String) -> String {
let clean = number.replacingOccurrences(of: " ", with: "")
guard clean.count >= 4 else { return "****" }
let last4 = String(clean.suffix(4))
return "**** **** **** \(last4)"
}
/// Mask email: u***r@yanipay.com
func maskEmail(_ email: String) -> String {
guard let atIndex = email.firstIndex(of: "@") else { return "***" }
let local = String(email[email.startIndex..<atIndex])
let domain = String(email[atIndex...])
guard local.count > 2 else { return "***\(domain)" }
let first = local.prefix(1)
let last = local.suffix(1)
let stars = String(repeating: "*", count: min(3, local.count - 2))
return "\(first)\(stars)\(last)\(domain)"
}
/// Mask IBAN: FR** **** **** **** **** 0366
func maskIBAN(_ iban: String) -> String {
let clean = iban.replacingOccurrences(of: " ", with: "")
guard clean.count >= 6 else { return "****" }
let prefix = String(clean.prefix(2))
let last4 = String(clean.suffix(4))
return "\(prefix)** **** **** **** **** \(last4)"
}
}Keychain Services
Le KeychainManagerfournit une abstraction securisee pour stocker les tokens d'authentification, les cles API et les identifiants de session dans le Keychain iOS chiffre.
import Security
import Foundation
class KeychainManager {
static let shared = KeychainManager()
private let service = "com.yanipay.keychain"
/// Save data to Keychain with biometric protection
func save(key: String, data: Data, requireBiometric: Bool = true) throws {
// Delete existing item first
try? delete(key: key)
var query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecValueData as String: data
]
if requireBiometric {
let access = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.biometryCurrentSet,
nil
)
query[kSecAttrAccessControl as String] = access
}
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.saveFailed(status)
}
}
/// Read data from Keychain
func read(key: String) throws -> Data {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess, let data = result as? Data else {
throw KeychainError.readFailed(status)
}
return data
}
/// Delete all YaniPay entries (used on compromise detection)
func deleteAll() {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service
]
SecItemDelete(query as CFDictionary)
}
}Bonnes Pratiques
A faire
- ✓Stocker les cles dans le Secure Enclave
- ✓Utiliser CryptoKit pour tout chiffrement
- ✓Exiger la biometrie pour les operations sensibles
- ✓Masquer les donnees dans les logs et l'UI
- ✓Verifier l'integrite de l'appareil au demarrage
- ✓Implementer le Certificate Pinning
A eviter
- ✗Stocker des mots de passe en clair
- ✗Utiliser UserDefaults pour des secrets
- ✗Logger des donnees sensibles non masquees
- ✗Desactiver App Transport Security (ATS)
- ✗Ignorer les avertissements du Secure Enclave
- ✗Hardcoder des cles API dans le code source
Securite des Identifiants
Ne stockez jamais de mots de passe en clair. Utilisez toujours le Keychain ou Secure Enclave pour persister les identifiants, tokens et secrets. Les UserDefaults ne sont pas chiffres et sont accessibles en texte clair sur un appareil jailbreake.