RedirectSafely

Sanitize return_to-style URLs, including some edge cases that you probably missed.

RedirectSafely is used in production and extracted from Shopify.

Installation

Add these lines to your application's Gemfile:

gem 'redirect_safely', '~> 1.0'

And then execute:

$ bundle

Usage

  • RedirectSafely.safe?(url, options)

Return true if the URL is considered "safe", false otherwise.

### Parameters: - url String (required) - The URL to test

### Options: - path_match Regexp (optional) - Match the path portion of the URL against a regexp - require_absolute Boolean (optional) - If true, require an absolute URL (domain must be included in whitelist) - require_ssl Boolean (optional) - If true, and an absolute URL is provided, require a URL starting with https:// - whitelist String - Whitelisted domains for checking absolute URLs - subdomains String - Whitelisted subdomains for checking absolute URLs. Must start with a leading ..

  • RedirectSafely.make_safe(url, default, options)

Return url if it's safe, otherwise return default.

Shares options with safe?, and is roughly equivalent to:

  safe_url = RedirectSafely.safe?(url) ? url : default
  • RedirectSafelyValidator

If you persist a redirect URL on a model, you can validate that it is safe?:

  class Request
    validates :return_to, redirect_safely: true
  end

You can pass any options supported by safe? (but not those added by make_safe). In the event that you need more control over the options (ie, dynamically producting a whitelist based on other model attributes), write a custom validate method:

  class Request
    validates :store_url, presence: true
    validate :return_to, presence: true

    validate :return_to_is_safe

    private

    def return_to_is_safe
      errors.add(:return_to, :invalid) unless RedirectSafely.safe?(return_to, whitelist: URI.parse(store_uri).host)
    end
  end