Swift security
Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/swift-security
When to activate: Keychain, certificate pinning, encryption, biometrics, secure storage, App Transport Security in SwiftFrom its SKILL.md
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill swift-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.7 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it
Swift Security Patterns
Keychain Storage
Never store secrets in UserDefaults. Use the Keychain.
import Security
struct KeychainManager {
static func save(_ value: String, forKey key: String, service: String) throws {
let data = Data(value.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: key,
kSecValueData as String: data,
// Store in Secure Enclave-backed class when available
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
]
SecItemDelete(query as CFDictionary) // delete existing before adding
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else { throw KeychainError.saveFailed(status) }
}
static func load(forKey key: String, service: String) throws -> String {
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,
let string = String(data: data, encoding: .utf8) else {
throw KeychainError.loadFailed(status)
}
return string
}
}
enum KeychainError: Error {
case saveFailed(OSStatus)
case loadFailed(OSStatus)
}
Biometric Authentication
import LocalAuthentication
func authenticateWithBiometrics() async throws -> Bool {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
throw error ?? LAError(.biometryNotAvailable)
}
return try await context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Confirm your identity to access sensitive data"
)
}
Certificate Pinning
final class PinnedURLSessionDelegate: NSObject, URLSessionDelegate {
private let pinnedPublicKeyHashes: Set<String>
init(pinnedHashes: Set<String>) {
self.pinnedPublicKeyHashes = pinnedHashes
}
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
// Evaluate the server certificate chain
var secResult = SecTrustResultType.invalid
SecTrustGetTrustResult(serverTrust, &secResult)
if let publicKey = SecTrustCopyKey(serverTrust),
let keyData = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? {
let hash = SHA256.hash(data: keyData).compactMap { String(format: "%02x", $0) }.joined()
if pinnedPublicKeyHashes.contains(hash) {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
return
}
}
completionHandler(.cancelAuthenticationChallenge, nil)
}
}
AES-GCM Encryption
import CryptoKit
struct Encryptor {
static func encrypt(_ data: Data, key: SymmetricKey) throws -> Data {
let sealed = try AES.GCM.seal(data, using: key)
return sealed.combined!
}
static func decrypt(_ encryptedData: Data, key: SymmetricKey) throws -> Data {
let box = try AES.GCM.SealedBox(combined: encryptedData)
return try AES.GCM.open(box, using: key)
}
static func generateKey() -> SymmetricKey {
SymmetricKey(size: .bits256)
}
}
App Transport Security
<!-- Info.plist — do NOT disable ATS globally -->
<!-- If a third-party domain requires HTTP, be specific -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>legacy-api.example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<false/>
</dict>
</dict>
</dict>
Preventing Screen Capture of Sensitive Fields
// SwiftUI — redact content from screenshots and screen recording
TextField("Card Number", text: $cardNumber)
.privacySensitive()
// UIKit
let field = UITextField()
field.isSecureTextEntry = true // also hides keyboard in screenshots
Common Anti-Patterns
- UserDefaults for tokens — always use Keychain
- Hardcoded API keys in source — load from config or secure backend
- Disabled ATS globally — Apple may reject; use targeted exceptions
- MD5/SHA1 for hashing — use SHA-256 or CryptoKit primitives
- No certificate pinning for sensitive APIs — at minimum pin the leaf certificate
- Logging sensitive data — never log tokens, passwords, or PII; redact in crash reporters
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.