Nelle applicazioni iOS moderne, un flusso di ripristino password sicuro è un componente fondamentale della gestione dell'account utente. Non solo aiuta gli utenti a riacquistare l'accesso quando dimenticano le loro credenziali, ma serve anche come prima linea di difesa contro gli attacchi di assunzione di account. Un processo di ripristino scarsamente implementato può esporre gli utenti a phishing, token theft, o brute-force enumeration.

Comprendere il modello di minaccia

Prima di scrivere qualsiasi codice, è essenziale capire le minacce che si sta difendendo contro. Il flusso di reset della password è un obiettivo di alto valore per gli aggressori perché può consentire loro dirottare un account con solo accesso alla casella di posta dell'utente o un token trapelato.

  • Inserzione di posta elettronica:[] Se l'e-mail di reset viene inviata su testo normale o memorizzata in modo insecutivo, un aggressore può rubare il token.
  • Riproduci la riproduzione:[] Se i gettoni non scadono o non sono un uso singolo, un aggressore può riutilizzarli anche dopo che l'utente legittimo cambia la password.
  • Rate-limit bypass:[ Senza un corretto ordinamento, un aggressore può bombardare il server con richieste di reset, causando la negazione del servizio o fastidio dell'utente.
  • User enumeration:[] Se il server restituisce risposte diverse per le email registrate vs. non registrate, un attaccante può raschiare gli indirizzi email validi.
  • Insicuro link profondi:[] Se l'applicazione registra un sistema di URL personalizzato che non è verificato, un'app maliziosa può intercettare il token.

Ogni decisione di progettazione deve mitigare questi rischi. Il resto di questo articolo dettagli come affrontare ogni minaccia durante la fornitura di un'esperienza utente liscia.

Principi chiave di un flusso di ripristino della password sicuro

Questi principi di alto livello guidano l'implementazione tecnica che segue.

  • Verificazione dell'identità dell'utente:[ La richiesta di reset deve confermare che la persona che lo avvia ha accesso all'indirizzo email registrato. Questo viene tipicamente fatto attraverso un token di tempo limitato e generato casualmente inviato a tale e-mail.
  • Comunicazione del Pubblico:[ Tutti i dati scambiati tra l'app iOS e il backend devono essere crittografati utilizzando TLS 1.2 o versioni successive.
  • Autenticazione basata su gettoni:[] Il token di reset deve essere crittografico casuale (almeno 128 bit), ha una breve scadenza (ad esempio, 15-30 minuti), e essere invalidato immediatamente dopo un cambiamento di password di successo.
  • Esposizione dati microscopica:[] L'app e il backend non dovrebbero mai rivelare se un indirizzo email è registrato. Utilizzare messaggi generici come “Se un account esiste, un link di reset è stato inviato.” Allo stesso modo, non esporre il token o qualsiasi dettaglio del conto nei parametri dell'URL o corpi di risposta oltre ciò che è strettamente necessario.

Implementare il flusso di recupero password in iOS

I seguenti passi si snodano attraverso l'interazione completa del server client, con una guida specifica per lo sviluppo iOS utilizzando Swift e integrando con Directus come backend.

Passo 1: l'utente inizia un reset

Creare una semplice vista in cui l'utente inserisce il proprio indirizzo email. Convalida il formato e-mail localmente prima di inviare la richiesta per evitare chiamate di rete non necessarie.

func requestPasswordReset(email: String) async throws {
 guard isValidEmail(email) else { throw ValidationError.invalidEmail }
 let url = URL(string: "https://api.example.com/auth/password/request")!
 var request = URLRequest(url: url)
 request.httpMethod = "POST"
 request.setValue("application/json", forHTTPHeaderField: "Content-Type")
 let body = ["email": email]
 request.httpBody = try JSONEncoder().encode(body)
 let (_, response) = try await URLSession.shared.data(for: request)
 guard let httpResponse = response as? HTTPURLResponse,
 httpResponse.statusCode == 200 else { throw NetworkError.requestFailed }
 // Always show the same success message regardless of email existence
}

Si noti che il client non si differenzia tra una e-mail registrata e non registrata; il server restituisce un generico 200.

Passo 2: Backend genera e invia un segno

In Directus, è possibile estendere i endpoint di autenticazione incorporati o creare un gancio personalizzato.

  1. Controllare se l'email esiste (ma non rivelare il risultato al cliente).
  2. Generare un token casuale (ad esempio, usando in Node.js).
  3. Conservare una versione del token del database insieme all'ID utente e al timestamp di scadenza.
  4. Inviare una email contenente un link profondo che include il token grezzo. Il formato del link dovrebbe essere qualcosa come .
  5. Limitamento della velocità di implementazione: consentire una sola richiesta di reset per e-mail per 60 secondi, e un massimo di, diciamo, 5 richieste all'ora.

Utilizzando un CMS senza testa come Directus semplifica questo perché è possibile gestire ruoli utente, modelli di posta elettronica, e gettone di scadenza direttamente attraverso il Pannello di amministrazione o tramite estensioni.

Su iOS, gestire i link profondi in entrata utilizzando [] o il più recente per Link Universali. Per uno schema di URL personalizzato, registra [ in Info.plist e implementa . Per una maggiore sicurezza, utilizzare Link Universali con Domini Associati, che impedisce ad altre applicazioni di intercettare il link.

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
 guard url.scheme == "yourapp", url.host == "reset-password",
 let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
 let token = components.queryItems?.first(where: { $0.name == "token" })?.value else {
 return false
 }
 // Navigate to the reset password view controller with the token
 showResetPasswordView(token: token)
 return true
}

Una volta che la vista di reset, l'app invia il gettone al server per la validazione prima di mostrare i nuovi campi di password, evitando così di sprecare il tempo dell'utente se il gettone è scaduto o malformato.

func validateToken(_ token: String) async throws -> Bool {
 let url = URL(string: "https://api.example.com/auth/password/validate")!
 var request = URLRequest(url: url)
 request.httpMethod = "POST"
 request.setValue("application/json", forHTTPHeaderField: "Content-Type")
 let body = ["token": token]
 request.httpBody = try JSONEncoder().encode(body)
 let (data, response) = try await URLSession.shared.data(for: request)
 guard let httpResponse = response as? HTTPURLResponse,
 httpResponse.statusCode == 200 else { return false }
 // You can optionally decode a response that includes the userID for later use
 return true
}

Passo 4: Impostare una nuova password

Dopo che la validazione di token è riuscita, presenta i nuovi campi di password e di conferma. Applicare le regole di forza della password sul client (ad esempio, lunghezza minima, diversità dei caratteri) ma sempre rivalutare sul server.

func resetPassword(token: String, newPassword: String) async throws {
 guard isPasswordStrong(newPassword) else { throw ValidationError.weakPassword }
 let url = URL(string: "https://api.example.com/auth/password/reset")!
 var request = URLRequest(url: url)
 request.httpMethod = "POST"
 request.setValue("application/json", forHTTPHeaderField: "Content-Type")
 let body = ["token": token, "password": newPassword]
 request.httpBody = try JSONEncoder().encode(body)
 let (_, response) = try await URLSession.shared.data(for: request)
 guard let httpResponse = response as? HTTPURLResponse,
 httpResponse.statusCode == 200 else { throw NetworkError.resetFailed }
 // Token is now invalidated; show success and navigate to login
}

Il server deve avere la nuova password (bcrypt, argon2, ecc.) e invalidare immediatamente il token di reset.

Integrazione con Directus per Backend Logic

Directus fornisce il supporto integrato per i reimpostazioni password[] attraverso le sue API REST e GraphQL. Per impostazione predefinita, invia un token con una scadenza configurabile. Tuttavia, per un'applicazione iOS nativo, probabilmente si desidera personalizzare il flusso per utilizzare i link profondi invece del collegamento web predefinito.

  • Disabilitare il comportamento e-mail predefinito di Directus e invece creare un gancio personalizzato (o un'estensione) che invia un link profondo contenente il token grezzo.
  • Utilizzando Directus endpoint per generare il token, quindi intercettare l'email attraverso un pacchetto npm personalizzato o sovrascrivendo il servizio di posta.
  • Memorizzando il token di hashed nella tabella ] e impostando una scadenza tramite un campo di datetime personalizzato.

Questo approccio consente di mantenere la gestione degli utenti centralizzata in Directus, mentre personalizzare l'esperienza di reset per la navigazione nativa iOS.

Migliori Pratiche per Sviluppatori

  • Limitare il collegamento:[] Attuazione backoff esponenziale sul server per le richieste di reset per indirizzo IP e per e-mail.
  • Conservazione dei gettoni:[] Non memorizzare mai i token grezzi nel database. Utilizzare una forte funzione hash (SHA-256) e confrontare le hash durante la validazione. OWASP fornisce linee guida dettagliate[ sulla generazione e lo storage dei token.
  • Inviare e-mail:[] Usa SMTP autenticato con TLS. Considerare l'integrazione con servizi come SendGrid o Amazon SES che offrono monitoraggio e monitoraggio delle consegne.
  • Logging e monitoraggio:[] Registra tutti i tentativi di reset (anonimizzati) per rilevare i modelli di abuso.
  • Autenticazione Biometrica:[] Come ulteriore protezione, richiedono Face ID o Touch ID prima di consentire all'utente di impostare una nuova password sul dispositivo, evitando che un aggressore che abbia accesso fisico a un telefono sbloccato reimpostasse la password.
  • Esperienza utente:[[] Mostra messaggi chiari e non tecnici. Evitare di dire all'utente perché un reset non è riuscito (ad esempio, “Token scaduto”). Invece, dire “Il link non è più valido.

Testare il flusso di ripristino della password

Test approfonditi è essenziale per catturare i casi di bordo e i problemi di tempismo.

  • Token scaduto – verifica la risposta dell'app 400/401 e reindirizza l'utente a richiedere un nuovo link.
  • Token riutilizzato – dopo un cambiamento di password di successo, tentare di utilizzare nuovamente lo stesso token; deve essere respinto.
  • Richieste ricorrenti – invia più richieste di reset e verifica che solo l'ultimo lavoro generato (o che tutti siano invalidati dopo un utilizzo).
  • Interruzioni di rete – simulare una connessione a scomparsa durante la convalida di token o la presentazione di password; l'applicazione non deve lasciare l'utente in uno stato inconsistente.
  • Dirottamento dei link profondo – prova che solo la tua applicazione può aprire lo schema di URL personalizzato e che qualsiasi altra applicazione che rivendica lo schema è ignorato (utilizzare Universal Links per una maggiore sicurezza).

Automatizzare questi test utilizzando XCUITest per i test di flusso e unità UI per lo strato di rete. Inoltre eseguire un audit di sicurezza con strumenti di test di penetrazione per verificare che i token non possono essere forzati o indovinati.

Pitfalls comune e come evitare di loro

  • Esporre il token nei registri o analisi:[[] Assicurarsi che il token non sia mai registrato dall'app iOS (ad esempio, attraverso le dichiarazioni o SDK di terze parti).
  • Utilizzando i token prevedibili: Non generare mai gettoni basati su timestamp, ID utente o contatori incrementali.
  • Non invalidare vecchie sessioni:[] Dopo un reset password, il server dovrebbe revocare tutti i token di aggiornamento attivi e accedere ai token per l'utente.
  • Ignorando il Portachiavi:[] Se l'applicazione memorizza temporaneamente il token di reset per la durata del flusso (ad esempio, passare tra i controller di vista), memorizzarlo nella Portachiavi con accessibilità limitata ().
  • Over-engineering the flow:[ Mentre la sicurezza è fondamentale, evitare di aggiungere attrito inutile. Ad esempio, non è necessario che l'utente risponda alle domande di sicurezza o verifichi un numero di telefono a meno che il reset non sia per un account ad alto valore (ad esempio, bancario).

Conclusioni

L’implementazione di un flusso sicuro di password in un’app iOS è una sfida multi-strato che bilancia l’usabilità con i controlli di sicurezza forti. Seguire i principi delineati in questo articolo, soprattutto intorno alla generazione dei token, il ripristino profondo, il limite di velocità e la prevenzione dell’enumerazione degli utenti, è possibile creare un flusso che protegge sia gli utenti che l’integrità della tua applicazione.