The go-to resource for upgrading Ruby, Rails, and your dependencies.

Server-Side Request Forgery (SSRF) in Rails: Risks and Mitigation


Server-Side Request Forgery (SSRF), cataloged as CWE-918, is a web security vulnerability that allows an attacker to induce a server-side application to make HTTP requests to an arbitrary domain of the attacker’s choosing. In the context of a Ruby on Rails application, this can be particularly dangerous, as it can lead to the exposure of internal services, sensitive data, and even remote code execution.

The core of the SSRF vulnerability lies in the trust that the application has in user-supplied input. When an application fetches a remote resource based on a URL provided by a user without proper validation, it creates an opening for attackers.

The Risks of SSRF in a Rails Environment

An SSRF vulnerability can expose your application and internal network to a variety of attacks:

  • Internal Network Scanning: Attackers can use the server to scan the internal network, identifying open ports and services that are not accessible from the public internet.
  • Data Exfiltration: Sensitive data from internal services, such as cloud provider metadata (e.g., AWS EC2 instance metadata), can be accessed and exfiltrated.
  • Interaction with Internal Services: Attackers can interact with internal services, potentially leading to unauthorized actions like database manipulation or cache poisoning.
  • Remote Code Execution: In some cases, SSRF can be escalated to achieve remote code execution, giving the attacker full control over the server.

A Practical Example of SSRF in Rails

Let’s consider a Rails controller that fetches an image from a URL provided by a user. This is a common feature in applications that allow users to upload profile pictures from a URL.

Here’s a vulnerable code snippet:

# app/controllers/images_controller.rb

require 'open-uri'

class ImagesController < ApplicationController
  def fetch
    image_url = params[:url]
    # Vulnerable code: The application fetches any URL provided by the user.
    image_data = URI.open(image_url).read
    
    # ... process the image data ...

    render plain: "Image fetched successfully!"
  end
end

In this example, the fetch action takes a URL from the params hash and uses open-uri to fetch the content. An attacker could abuse this by providing a URL that points to an internal service:

  • http://localhost:8080/admin
  • http://169.254.169.254/latest/meta-data/ (AWS EC2 metadata endpoint)
  • file:///etc/passwd

Mitigating SSRF Vulnerabilities in Rails

The key to preventing SSRF is to never fully trust user-supplied input. Here are several strategies to mitigate this vulnerability in your Rails application.

1. Whitelist Allowed Domains

Instead of allowing requests to any domain, maintain a whitelist of allowed domains. This is the most effective way to prevent SSRF.

# app/controllers/images_controller.rb

require 'open-uri'

class ImagesController < ApplicationController
  ALLOWED_DOMAINS = ['example.com', 'anotherexample.com']

  def fetch
    image_url = params[:url]
    uri = URI.parse(image_url)

    unless ALLOWED_DOMAINS.include?(uri.host)
      render plain: "Invalid domain.", status: :bad_request
      return
    end

    image_data = URI.open(image_url).read
    
    # ... process the image data ...

    render plain: "Image fetched successfully!"
  end
end

2. Validate URL Schemes

Ensure that you are only allowing HTTP and HTTPS protocols. This can prevent attackers from using other schemes like file:// or gopher://.

# app/controllers/images_controller.rb

# ... (inside the fetch method)
uri = URI.parse(image_url)

unless ['http', 'https'].include?(uri.scheme)
  render plain: "Invalid URL scheme.", status: :bad_request
  return
end
# ...

3. Use a Safe HTTP Client

Libraries like open-uri are powerful but can be dangerous if used with untrusted input. Consider using a more controlled HTTP client like HTTParty or Faraday, and configure it to prevent redirects and accesses to local addresses.

A safer approach is to use a library that specifically addresses SSRF, such as ssrf_filter.

Here’s how you can use ssrf_filter:

# Gemfile
gem 'ssrf_filter'

And in your controller:

# app/controllers/images_controller.rb

class ImagesController < ApplicationController
  def fetch
    image_url = params[:url]
    
    begin
      response = SsrfFilter.get(image_url)
      image_data = response.body
      # ... process the image data ...
      render plain: "Image fetched successfully!"
    rescue SsrfFilter::Error => e
      render plain: "Error: #{e.message}", status: :bad_request
    end
  end
end

The ssrf_filter gem automatically blocks requests to private, local, and reserved IP addresses, providing a strong layer of defense against SSRF attacks.

Conclusion

Server-Side Request Forgery is a serious vulnerability that can have a significant impact on the security of your Rails application. By understanding the risks and implementing robust mitigation strategies, you can protect your application and its underlying infrastructure.

The key takeaways are:

  • Never trust user input: Always validate and sanitize user-supplied URLs.
  • Use a whitelist: Only allow requests to known and trusted domains.
  • Leverage safe libraries: Use tools like ssrf_filter to automatically block malicious requests.

By following these best practices, you can build more secure and resilient Rails applications. If you are concerned about the security of your application, consider a professional security audit to identify and address vulnerabilities like SSRF.

Sponsored by Durable Programming

Need help maintaining or upgrading your Ruby on Rails application? Durable Programming specializes in keeping Rails apps secure, performant, and up-to-date.

Hire Durable Programming