---
title: Validate toàn bộ input từ người dùng và URL scheme - không tin dữ liệu bên ngoài
impact: HIGH
impactDescription: Không validate URL scheme parameters, deep link payload hoặc form input dẫn đến injection, logic bypass, hoặc truy cập unauthorized. Input từ bất kỳ source nào đều phải được validate ở server-side layer.
tags: swift, ios, input-validation, deeplink, url-scheme, universal-link, security
---

## Validate toàn bộ input từ người dùng và URL scheme - không tin dữ liệu bên ngoài

Mọi input từ người dùng, URL scheme, universal link, clipboard, hoặc push notification payload đều phải được validate: kiểm tra type, length, format và phạm vi cho phép trước khi xử lý. Đặc biệt cẩn thận với navigation target từ deep link.

**Incorrect (không validate deep link parameters):**

```swift
import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {

    // !! Không validate parameter từ deep link - open URL tùy ý
    func application(_ app: UIApplication,
                     open url: URL,
                     options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        guard url.scheme == "myapp" else { return false }

        if url.host == "product" {
            // !! productId có thể là "../../../etc" hoặc SQL injection
            let productId = url.queryParameters["id"] ?? ""
            navigateToProduct(id: productId)  // Không validate!
        }

        if url.host == "webview" {
            // !! Mở URL tùy ý trong WKWebView - open redirect!
            let targetURL = url.queryParameters["url"] ?? ""
            openInWebView(urlString: targetURL)  // Nguy hiểm!
        }
        return true
    }
}
```

**Correct (validate trước khi xử lý):**

```swift
import UIKit

// SAFE: Validator tập trung
enum ValidationError: LocalizedError {
    case invalidFormat(String)
    case outOfRange(String)
    case suspiciousInput(String)

    var errorDescription: String? {
        switch self {
        case .invalidFormat(let f): return "Invalid format: \(f)"
        case .outOfRange(let f): return "Out of range: \(f)"
        case .suspiciousInput(let f): return "Suspicious input: \(f)"
        }
    }
}

struct InputValidator {
    // Chỉ accept UUID format cho product ID
    static func validateProductId(_ id: String) throws -> UUID {
        guard let uuid = UUID(uuidString: id) else {
            throw ValidationError.invalidFormat("productId must be UUID")
        }
        return uuid
    }

    // Chỉ accept HTTPS URL trong domain whitelist
    static func validateInternalURL(_ urlString: String) throws -> URL {
        guard let url = URL(string: urlString),
              url.scheme == "https" else {
            throw ValidationError.invalidFormat("URL must be HTTPS")
        }
        let allowedHosts = ["app.example.com", "static.example.com"]
        guard let host = url.host, allowedHosts.contains(host) else {
            throw ValidationError.suspiciousInput("URL host not in whitelist: \(url.host ?? "nil")")
        }
        return url
    }

    static func validatePhoneNumber(_ number: String) throws -> String {
        let regex = #"^\+?[0-9]{10,15}$"#
        let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
        guard predicate.evaluate(with: number) else {
            throw ValidationError.invalidFormat("Invalid phone number")
        }
        return number
    }
}

class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ app: UIApplication,
                     open url: URL,
                     options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        guard url.scheme == "myapp" else { return false }

        do {
            if url.host == "product" {
                let rawId = url.queryParameters["id"] ?? ""
                let productId = try InputValidator.validateProductId(rawId)
                navigateToProduct(id: productId)  // UUID-typed, safe
            }

            if url.host == "webview" {
                let rawURL = url.queryParameters["url"] ?? ""
                let safeURL = try InputValidator.validateInternalURL(rawURL)
                openInWebView(url: safeURL)  // Validated URL, whitelist
            }
        } catch {
            // Log attempt nhưng không crash
            logger.warning("Invalid deep link parameter", metadata: [
                "url": "\(url)",
                "error": "\(error.localizedDescription)"
            ])
            return false
        }
        return true
    }
}
```

**Tools:** OWASP MASVS-PLATFORM-1, SwiftLint, URLComponents (safer URL parsing)
