Server-side request forgery (SSRF) - Ruby

Server-side request forgery (SSRF) - Ruby

Need

Prevention of unauthorized server-side requests to arbitrary domains

Context

  • Usage of Ruby for building web applications and scripting
  • Usage of net-http for handling HTTP requests and responses in a Node.js application

Description

Non compliant code

        def fetch_data
  url = params[:url]
  response = Net::HTTP.get(URI.parse(url))
  render json: response
end
        
        

In the above code, we have a function fetch_data that fetches some data from a URL. This URL is taken from the request parameters, which means it is user-controlled data.

This function uses the Ruby's Net::HTTP.get method to send a GET request to the URL specified in the request parameters. The server then responds with the data from the requested URL.

The vulnerability here is that the application server can be tricked into making requests to arbitrary domains. This is a Server-Side Request Forgery (SSRF) vulnerability. An attacker could potentially use this to probe internal network resources, perform attacks against other external systems, or induce the server into interacting with other services within its private network.

The problem arises because the application does not validate or restrict the url parameter. It directly uses this parameter to make HTTP requests. As a result, an attacker can provide any URL, including localhost or private IP addresses, causing the server to make requests to any server, including itself or other servers in the internal network.

Steps

  • Validate the input URL to ensure it is a valid and trusted URL.
  • Implement a whitelist of allowed domains that the application can make requests to.
  • Use a library or framework feature that provides built-in protection against SSRF, such as URI parsing or URL validation.
  • Consider using a proxy server or firewall to restrict outgoing requests to trusted domains.
  • Implement proper error handling and logging to detect and respond to SSRF attempts.
  • Educate developers about the risks and best practices for preventing SSRF vulnerabilities.

Compliant code

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

  # Validate the URL
  raise "Invalid URL" unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)

  # Whitelist of allowed domains
  allowed_domains = ["www.trusteddomain1.com", "www.trusteddomain2.com"]
  domain = uri.host

  # Check if the domain is in the whitelist
  raise "Forbidden request" unless allowed_domains.include?(domain)

  response = Net::HTTP.get(uri)
  render json: response
rescue => e
  # Log the error and return a 400 bad request status
  logger.error "Error fetching data: #{e.message}"
  render json: { error: "Bad request" }, status: :bad_request
end
        
        

The updated code includes several measures to prevent Server-Side Request Forgery (SSRF) attacks:

1. URL Validation: The URI.parse method is used to parse the URL and ensure it is a valid HTTP or HTTPS URL. If the URL is not valid, an exception is raised.

2. Domain Whitelisting: A whitelist of allowed domains is defined. The domain of the input URL is extracted and checked against this whitelist. If the domain is not in the whitelist, an exception is raised.

3. Error Handling: If any error occurs during the execution of the method (such as an invalid URL or a forbidden request), the error is logged and a 400 bad request status is returned to the client. This helps to detect and respond to SSRF attempts.

These measures help to ensure that the application's server only makes requests to valid and trusted domains, thereby mitigating the risk of SSRF attacks.

References