iOS Module 2
Security Course
Secure Coding & Application Hardening
Development Security Practices
Master secure Swift/Objective-C development: input validation, hardcoded secrets risks, Keychain storage. Learn application hardening, code obfuscation, runtime integrity checks. Understand threat modeling, secure development lifecycle, App Store compliance. Build secure applications from foundation.
Secure Swift Coding Awareness
Foundation of secure development
✍️ Input Validation Mindset
Never trust user input. All data from users, network, files must be validated. Input validation prevents injection attacks, buffer overflows, logic errors. Security-first mindset: assume attacker controls input.
Input Validation Best Practices
- Whitelist Approach: Define what IS allowed, reject everything else. More secure than blacklist (allowing all except known bad).
- Type Checking: Verify input type matches expected. String when expecting number? Reject. Swift type system helps enforce at compile time.
- Length Checks: Validate input length. Too-long input might indicate buffer overflow attack. Set reasonable maximums.
- Format Validation: Email format, phone number format, URL format. Use regex cautiously (can be expensive).
- Range Checks: Numeric input within expected range? Age 0-120 valid, age 500 invalid. Prevents logic errors.
- Encoding Awareness: Different encodings (UTF-8, Unicode) can cause issues. Validate character encoding.
Common Input Attacks
- SQL Injection: Attackers inject SQL commands via input. Example: search field gets `"; DROP TABLE users;--`. Parameterized queries prevent this.
- Command Injection: Injecting shell commands. Example: filename input used in `system()` call. Attacker enters `$(rm -rf /)` as filename.
- Path Traversal: Filename input like `../../etc/passwd` accessing files outside intended directory. Validate paths.
- Buffer Overflow: Input longer than buffer capacity. Overwrites adjacent memory. Swift prevents via array bounds checking.
- XSS (Web Context): Web views displaying user input as HTML. Attackers inject `
🚨 Hardcoded Secrets Vulnerability
Never hardcode sensitive data: API keys, passwords, encryption keys, database credentials. If hardcoded, anyone with app code access gets secrets. Attackers decompile apps extracting hardcoded secrets.
Hardcoded Secrets Risks
- App Code Exposure: Apps can be downloaded, decompiled, analyzed. Hardcoded secrets easily extracted.
- Git History Leakage: Developers commit secrets to Git. Even if removed later, Git history contains secrets. Attackers access Git finding old commits.
- Build Artifacts: Compiled binaries contain hardcoded strings. Reverse engineering reveals secrets.
- Logs Exposure: Secrets logged for debugging. Logs stored on device, transmitted to servers. Compromised logs reveal secrets.
- Screenshot Inclusion: Screenshots containing sensitive data included in app documentation. Anyone with documentation gets secrets.
Secure Secret Management
- Environment Variables: Secrets passed via environment variables at build/runtime. Not in code.
- Configuration Files: Secrets in separate config files, not compiled into app. Config read at runtime.
- Remote Configuration: Fetch secrets from secure server at runtime. Server authenticates app before providing secrets.
- Hardware-Backed Storage: Secrets stored in Secure Enclave/TEE. Inaccessible even if app compromised.
- Secret Rotation: Periodically change secrets. Limits damage if old secret compromised.
let apiKey = "sk_live_abc123xyz"
let databasePassword = "SecurePass123"
# ✅ GOOD - Environment
let apiKey = ProcessInfo.processInfo.environment["API_KEY"]
let pass = fetchSecretFromKeychain("database_pass")
🔑 Secure Storage: Keychain Awareness
iOS Keychain: secure credential storage. Encrypted at rest using device encryption key. Accessible only by app that stored it (or apps with entitlements). Secure alternative to UserDefaults.
Keychain Fundamentals
- Hardware Encryption: Keychain uses device encryption key. Data encrypted at device level. Even if device storage accessed, data remains encrypted.
- Access Control: App that stores credential can retrieve it. Other apps blocked by default. Keychain Access Groups allow sharing between apps.
- Availability: Keychain data available only when device unlocked. Requires biometric/passcode authentication.
- Persistent Storage: Survives app uninstall (unless explicitly cleared). Useful for authentication tokens lasting beyond app session.
- iCloud Sync: Optional iCloud sync for credentials. Synced securely across user's devices. Requires user permission.
What to Store in Keychain
- Authentication Tokens: OAuth tokens, JWT tokens. Must survive app restarts.
- Passwords: User passwords for app login. Never store plaintext.
- Encryption Keys: App-specific encryption keys. Master encryption keys especially.
- API Keys: Third-party API keys not part of app logic. Protect like passwords.
- PII: Personally identifiable info (email, SSN). Encrypt with Keychain.
What NOT to Store in Keychain
- Large Blobs: Keychain designed for small data (passwords, tokens). Large files use encrypted FileProtection instead.
- Cache Data: Non-sensitive cache stored in NSCache or temp directories.
- Public Data: Non-sensitive data stored in UserDefaults or Core Data.
let password = "UserPassword123"
let passwordData = password.data(using: .utf8)!
let query: [String: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: "user@example.com",
kSecValueData: passwordData
]
SecItemAdd(query as CFDictionary, nil)
# Retrieve from Keychain
var result: CFTypeRef?
SecItemCopyMatching(query as CFDictionary, &result)
Application Hardening Concepts
Protecting apps from reverse engineering and tampering
🔐 Code Obfuscation Awareness
Code obfuscation: making code difficult to understand/reverse engineer. Attackers decompile apps extracting logic, stealing algorithms, finding vulnerabilities. Obfuscation raises barrier to entry.
Obfuscation Techniques
- Name Mangling: Rename variables/functions to meaningless names: `a, b, f1, f2`. Makes decompiled code harder to understand.
- Control Flow Obfuscation: Add dummy code, unnecessary branching. Makes control flow analysis harder.
- String Encryption: Encrypt hardcoded strings, decrypt at runtime. Prevents simple string searching in binary.
- Code Flattening: Remove loop/control structures, replace with gotos. Makes logic flow unclear.
- API Hiding: Hide API calls behind indirection. Direct API calls easily found in reversing, indirection makes finding harder.
- Junk Code: Add meaningless code not affecting logic. Increases binary size, obfuscates real logic.
Obfuscation Limitations
- Not Unbreakable: Obfuscation only raises difficulty, determined attackers can still deobfuscate.
- Runtime Analysis: Obfuscated code obvious when running. Debuggers show real behavior regardless of obfuscation.
- Performance Cost: Obfuscation adds overhead. Heavy obfuscation can slow apps significantly.
- Debug Difficulty: Developers debugging obfuscated code have harder time. Need to balance security with debuggability.
🛡️ Runtime Integrity Checks (High-Level)
Runtime checks: app verifying its own integrity while running. Detects tampering, jailbreaks, debuggers. Stops compromised apps from executing sensitive code.
Runtime Integrity Checks
- Code Signature Verification: App verifies its code signature hasn't been tampered. Validates all bundle contents match signature.
- Checksum Validation: Calculate checksum of app binary, compare with expected. Mismatch indicates binary tampering.
- Jailbreak Detection: Check for jailbreak indicators: Cydia app presence, unusual file access, runtime modifications. Block execution on jailbroken devices.
- Debugger Detection: Detect attached debuggers. Attackers use debuggers stepping through code, modifying variables. Block/crash if debugged.
- Instrumentation Detection: Detect frida/xposed runtime instrumentation. Attackers inject code at runtime changing behavior.
- File System Integrity: Monitor app bundle files for changes. Detect injected code or modified resources.
- API Hooking Detection: Detect if critical APIs hooked. Hooked APIs indicate attacker trying to intercept calls.
Integrity Check Implementation
- Early Validation: Check integrity early in app launch. Before loading sensitive code.
- Continuous Checks: Periodic integrity checks during app execution. Not just at startup.
- Fail Secure: If integrity check fails, don't try to recover. App should crash/terminate. Don't continue with potentially compromised app.
- Anti-Hooking: Store sensitive functions in memory at runtime. Hook attacks target code at startup, runtime storage avoids some hooks.
- Self-Debugging: App attaches debugger to itself. Prevents external debuggers attaching (only one debugger allowed).
Secure Development Lifecycle
Integrating security throughout development
🎯 Threat Modeling Basics
Threat modeling: systematic process identifying potential threats to application. "What could go wrong?" framework. Enables prioritizing security efforts based on actual risks.
Threat Modeling Process
- Asset Identification: What does app protect? User data types? Credentials? Payment info? Intellectual property?
- Threat Identification: What attacks threaten these assets? Who attacks? (competitors, malicious users, hackers). What's their capability?
- Vulnerability Identification: What weaknesses enable threats? Missing input validation? Weak encryption? Poor authentication?
- Risk Assessment: Likelihood × Impact = Risk. High likelihood + high impact = high risk. Prioritize addressing high-risk items.
- Mitigation Planning: For each risk, what controls reduce risk? Encryption, authentication, rate limiting, etc.
- Implementation Verification: Build, implement, verify controls actually reduce identified risks. Test threat scenarios.
Common Threat Categories (STRIDE)
- Spoofing: Attacker pretends to be legitimate user/app. Mitigated by authentication.
- Tampering: Attacker modifies data in transit or at rest. Mitigated by encryption, signatures, integrity checks.
- Repudiation: Attacker denies actions they took. Mitigated by logging, audit trails.
- Information Disclosure: Sensitive data exposed. Mitigated by encryption, access controls.
- Denial of Service: Attacker prevents legitimate access. Mitigated by rate limiting, resource controls.
- Elevation of Privilege: Attacker gains unauthorized elevated access. Mitigated by principle of least privilege.
📱 App Review Process Awareness
Apple's App Store review: gatekeeping security. Apps reviewed before release ensuring policy compliance, no obvious malware. Understanding review process enables building compliant secure apps.
App Review Criteria
- Functionality: App must work as described. No crashes, hangs, broken features.
- Performance: Apps must run smoothly. Excessive battery drain, memory leaks flagged.
- Privacy Compliance: Privacy policy required. Must disclose data collection. Permissions must match actual collection.
- Data Security: Sensitive data encrypted. No hardcoded credentials. Secure storage practices.
- Network Security: HTTPS/TLS required for all network communication. Certificate pinning encouraged.
- Code Quality: No obvious exploitable bugs. Reviewers attempt known exploits (buffer overflows, format strings).
- Third-Party SDKs: SDKs scanned for suspicious behavior. SDKs collecting excessive data flagged.
- Permissions Justification: Every permission requested must be justified. Requesting camera without camera feature = rejection.
Common Rejection Reasons
- Vague Privacy Policy: Privacy policy must clearly explain data practices. Generic policy = rejection.
- Unnecessary Permissions: Requesting camera, microphone, location without justified use = rejection.
- Cryptic Code: Highly obfuscated code flagged as potentially malicious. Some obfuscation OK, extreme obfuscation = rejection.
- Suspicious Behavior: Apps calling unusual system functions, accessing restricted APIs flagged.
- Outdated SDK: Using very old SDKs with known vulnerabilities = rejection.
- Test Content: Leaving test accounts, debug code in submission = rejection.
🔄 Secure Development Lifecycle (SDLC)
Secure SDLC: integrating security into every development phase. Not bolting security on at end. Security from design through deployment, maintenance.
SDLC Phases
- Requirements: Define security requirements upfront. What data sensitive? What attacks prevent? Requirements drive design.
- Design: Threat model design. Identify security controls needed. Architecture supporting security (cryptography, authentication).
- Development: Code securely using secure coding practices. Input validation, error handling, crypto correctly.
- Testing: Security testing: penetration testing, fuzzing, vulnerability scanning. Find vulnerabilities before release.
- Deployment: Secure deployment process. Code signing, integrity verification. Secure configuration.
- Maintenance: Monitor for vulnerabilities. Security patches deployed quickly. Respond to incidents.
Security Best Practices in SDLC
- Security Training: Developers learn secure coding, common vulnerabilities, security tools.
- Code Review: Security-focused code review. Reviewers check for vulnerabilities before merging.
- Static Analysis: Automated tools scanning code finding common vulnerabilities (unused variables, potential overflow).
- Dynamic Testing: Apps tested while running. Fuzzing random input, monitoring behavior.
- Dependency Management: Track third-party libraries. Update when security patches released. Scan for known vulnerabilities.
- Incident Response: Plan for security incidents. If vulnerability discovered post-release, rapid patch deployment.