Hvad du får ud af denne guide
“Tilfældighed” lyder som noget, man bare får gratis fra systemet. I praksis er det et af de steder, hvor små misforståelser kan give store konsekvenser: svage tokens, forudsigelige session-id’er, eller “random” værdier, der gentager sig i tests og ender i produktion.
Her får du en praktisk gennemgang af, hvordan du bruger korrekt tilfældighed på Apple-platforme (iOS/iPadOS, macOS, watchOS og visionOS) med moderne Apple-API’er – og hvordan du undgår de klassiske faldgruber.
Vigtigt først: Hvad mener vi med “randomness”?
Der er to hovedkategorier, og de skal ikke blandes sammen:
- Kryptografisk sikker tilfældighed (CSPRNG): Bruges til nøgler, tokens, nonces, salts, CSRF, reset-links, invitationskoder mm. Skal være uforudsigelig.
- Statistisk “tilfældighed” til spil/simulering: Her handler det mere om fordeling og reproducerbarhed (fx seed), og sikkerhed er ofte irrelevant.
Guiden fokuserer primært på CSPRNG, fordi det er der, fejl gør mest skade.
Den korte anbefaling (de fleste bør gøre dette)
Swift: Brug SecRandomCopyBytes eller CryptoKit
På Apple-platforme er den mest direkte, systemnære løsning stadig Security-frameworkets SecRandomCopyBytes. Den henter kryptografisk sikker tilfældighed fra operativsystemets RNG.
Hvis du allerede bruger CryptoKit, kan du typisk blive i det univers til nøgler og nonces, men når du “bare” skal have rå bytes til fx et token, er SecRandomCopyBytes den simple, robuste grundsten.
Swift eksempel: Generér 32 sikre bytes
import Security
func secureRandomBytes(count: Int) throws -> [UInt8] {
var bytes = [UInt8](repeating: 0, count: count)
let status = SecRandomCopyBytes(kSecRandomDefault, count, &bytes)
guard status == errSecSuccess else {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
}
return bytes
}
Hvornår 32 bytes? 16 bytes er ofte fint til nonces; 32 bytes er et godt standardvalg til tokens, hvor du vil have solid margin.
Det du typisk ikke skal gøre
1) Brug ikke random()/rand() til sikkerhed
Klassiske C-funktioner og en del “hurtige” PRNG’er er ikke designet til at være uforudsigelige. De kan være fine til en shuffle i et spil – men ikke til auth-tokens.
2) Forveksl ikke seedede generatorer med sikkerhed
Seedede generatorer er gode, når du vil kunne genskabe samme sekvens (tests, simulering). Men det er præcis det modsatte af, hvad du vil i kryptografi.
3) Antag ikke at UUID() altid er “et token”
UUID v4 er ofte random-baseret og kan være udmærket som “unik id” i mange sammenhænge, men det er ikke automatisk et sikkert session-token. Hvis en angriber kan gætte eller udlede værdier, er du færdig. Til login-sessions og password-reset: brug rå CSPRNG-bytes og kod dem sikkert (fx Base64URL).
Sådan laver du et token rigtigt (og praktisk)
1) Generér bytes
Start med 32 bytes fra SecRandomCopyBytes.
2) Kod tokenet til transport
Til URL’er og headers er Base64URL ofte perfekt. Standard Base64 kan indeholde +, / og padding =, som kan være upraktisk i URL’er.
import Foundation
func base64url(_ data: Data) -> String {
let b64 = data.base64EncodedString()
return b64
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
.replacingOccurrences(of: "=", with: "")
}
3) Gem det korrekt på klienten
Hvis tokenet giver adgang (auth/refresh), så hører det typisk hjemme i Keychain – ikke i UserDefaults. Keychain giver dig bedre beskyttelse, bedre lifecycle og mindre risiko for læk via backups eller debugging-vaner.
Swift’s moderne random: hvad kan du stole på?
Swift har længe haft en “RandomNumberGenerator”-model og convenience som Int.random(in:). På Apple-platforme er den underliggende systemgenerator i praksis bundet til systemets sikre kilde. Men i sikkerhedskritiske flows er det stadig sundt at være eksplicit og bruge Security/CryptoKit, så din intention er krystalklar i koden og for code reviewers.
Til ikke-kryptografiske formål (shuffle, sampling i UI, testdata) er random(in:) typisk mere end fint.
Fejlfinding: når “random” opfører sig mærkeligt
Symptom: gentagelser eller mønstre i tests
Det sker ofte, fordi du ubevidst bruger en seedet generator eller genbruger en generator-instans på en måde, der gør udfaldet reproducerbart. Løsning: adskil test-PRNG (seedet) fra produktions-tilfældighed (CSPRNG).
Symptom: tokens kolliderer “meget sjældent”
Det er næsten altid et designproblem: for få bits, forkert encoding, eller tokenet bliver trimmet/normaliseret undervejs. En klassiker er at konvertere bytes til hex, men kun gemme de første N tegn “for pænhed”. Løsning: brug mindst 128 bits til unikke værdier (helst 256 bits til auth), og bevar hele tokenet.
Symptom: fejl fra SecRandomCopyBytes
Det er sjældent på moderne Apple OS’er, men du skal stadig håndtere fejl. Hvis du får en OSStatus-fejl, så fail fast og log minimalt (aldrig de genererede bytes), fordi du ellers risikerer at falde tilbage til usikre alternativer “midlertidigt” – som aldrig bliver fjernet igen.
Pro TipHvis du laver tokens til URL’er, så brug Base64URL (uden “=” padding) og gem originalen i Keychain—det reducerer både parsing-fejl og “mystiske” login-problemer på tværs af apps og domæner.
Min vurdering
Apple har i årevis leveret stærk, systemintegreret tilfældighed, og på moderne iOS/macOS er det sjældent selve RNG’en, der fejler. Det er integrationen: udviklere, der bruger en “random” API med den forkerte forventning, forkorter tokens for læsbarhed, eller gemmer dem et sted, hvor de ikke hører hjemme.
Hvis du kun tager én ting med: brug Security/CryptoKit til sikkerhed, og brug seedede generatorer kun, når du bevidst ønsker reproducerbarhed. Det er kedeligt – og det er hele pointen.
Hurtig tjekliste
- Auth/Reset/Invites: 32 bytes fra
SecRandomCopyBytes→ Base64URL → Keychain/secure storage. - Nonces/salts: mindst 16 bytes, helst 32 afhængigt af design.
- Shuffle/spil/UI: Swift’s
random(in:)er fint. - Aldrig:
rand(), hjemmelavede PRNG’er eller “forkortede” tokens i produktion.