Uncontrolled external site redirect - Swift

Uncontrolled external site redirect - Swift

Need

Controlled and secure external site redirects

Context

  • Usage of Swift for iOS and macOS app development
  • Usage of UIKit for building modern and responsive user interfaces
  • Usage of SafariServices for integrating Safari browser functionality into an iOS app

Description

Non compliant code

        import UIKit
import SafariServices

class ViewController: UIViewController {
    @IBOutlet weak var urlField: UITextField!

    @IBAction func openURL(_ sender: Any) {
        guard let urlString = urlField.text, let url = URL(string: urlString) else {
            return
        }
        let vc = SFSafariViewController(url: url)
        present(vc, animated: true)
    }
}
        
        

The above Swift code represents a basic iOS application with a single view controller. The view controller has a text field urlField that allows users to enter a URL. When the user presses a button, the openURL function is triggered.

The openURL function takes the text from the urlField, attempts to construct a URL from it, and if successful, opens the URL in a Safari view controller.

The vulnerability lies in the fact that the URL is taken directly from user input without any validation or control. This means that an attacker can enter a malicious URL into the text field, which the application will then open. This is an example of an uncontrolled external site redirect, as the application does not control or restrict which URLs can be opened.

This vulnerability can be exploited in a variety of ways. For example, an attacker could redirect users to a phishing site that mimics a legitimate site, tricking them into entering sensitive information. Alternatively, they could redirect users to a site that serves malware, potentially leading to the compromise of their device.

Steps

  • Validate the input URL to ensure it is a trusted and valid URL.
  • Implement a whitelist of allowed URLs that the application can redirect to.
  • Sanitize the input URL to remove any potentially malicious characters or payloads.
  • Implement a confirmation dialog or prompt before redirecting to an external site.
  • Use a custom URL scheme or deep linking to redirect within the application instead of relying on external redirects.
  • Implement proper error handling and logging to detect and mitigate any potential attacks or abuse of the redirect functionality.

Compliant code

        import UIKit
import SafariServices

class ViewController: UIViewController {
    @IBOutlet weak var urlField: UITextField!
    
    // Define a list of trusted URLs
    let trustedURLs = ["https://www.example.com", "https://www.example2.com"]

    @IBAction func openURL(_ sender: Any) {
        guard let urlString = urlField.text, let url = URL(string: urlString) else {
            return
        }
        
        // Check if the URL is in the list of trusted URLs
        if trustedURLs.contains(url.absoluteString) {
            let vc = SFSafariViewController(url: url)
            present(vc, animated: true)
        } else {
            // Show an error message if the URL is not trusted
            let alert = UIAlertController(title: "Error", message: "The URL is not trusted.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        }
    }
}
        
        

The updated code now includes a list of trusted URLs. When the openURL function is called, it first checks if the URL entered by the user is in the list of trusted URLs. If it is, the application will open the URL in the SFSafariViewController. If it is not, the application will present an alert to the user indicating that the URL is not trusted.

This approach mitigates the risk of uncontrolled external site redirects by ensuring that only URLs from a predefined list of trusted sources can be opened by the application. This prevents an attacker from manipulating the application to redirect to a malicious site.

In addition, the application now provides feedback to the user when a URL is not trusted. This can help educate users about the risks of untrusted URLs and discourage them from attempting to open such URLs in the application.

This solution, however, assumes that the list of trusted URLs can be predefined and is relatively static. If the application needs to support a wide range of URLs or if the list of trusted URLs changes frequently, a more dynamic solution may be needed. This could involve fetching the list of trusted URLs from a server, or implementing more complex logic to validate URLs based on certain criteria (e.g., the URL must use HTTPS, the domain must be a certain length, etc.).

References