Secure WebView Implementation

Secure WebView Implementation

WebViews can introduce vulnerabilities if not properly configured. Implementing secure WebView practices prevents common attacks.

import WebKit

class SecureWebViewController: UIViewController {
    private var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Configure secure WebView
        let configuration = WKWebViewConfiguration()
        
        // Disable JavaScript if not needed
        configuration.preferences.javaScriptEnabled = false
        
        // Configure content security policy
        let script = """
            var meta = document.createElement('meta');
            meta.name = 'Content-Security-Policy';
            meta.content = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';";
            document.getElementsByTagName('head')[0].appendChild(meta);
        """
        
        let userScript = WKUserScript(source: script, 
                                     injectionTime: .atDocumentEnd,
                                     forMainFrameOnly: true)
        configuration.userContentController.addUserScript(userScript)
        
        // Initialize WebView
        webView = WKWebView(frame: view.bounds, configuration: configuration)
        webView.navigationDelegate = self
        view.addSubview(webView)
    }
    
    func loadSecureContent(url: URL) {
        // Only load HTTPS URLs
        guard url.scheme == "https" else {
            print("Refusing to load non-HTTPS URL")
            return
        }
        
        let request = URLRequest(url: url)
        webView.load(request)
    }
}

extension SecureWebViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, 
                decidePolicyFor navigationAction: WKNavigationAction, 
                decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        
        // Validate navigation requests
        guard let url = navigationAction.request.url else {
            decisionHandler(.cancel)
            return
        }
        
        // Only allow HTTPS and specific schemes
        let allowedSchemes = ["https", "mailto", "tel"]
        if let scheme = url.scheme, allowedSchemes.contains(scheme) {
            decisionHandler(.allow)
        } else {
            decisionHandler(.cancel)
        }
    }
}