HIGH api rate abuserailssaml

Api Rate Abuse in Rails with Saml

Api Rate Abuse in Rails with Saml — how this specific combination creates or exposes the vulnerability

Rate abuse in a Ruby on Rails application that uses SAML for authentication can occur when attackers bypass or overload per-request protections by exploiting the identity provider (IdP) flow and session handling. SAML relies on redirects and assertions, which introduce multiple entry points where requests can be issued without adequate rate checks.

One common scenario: an attacker uses a valid SAML identity to obtain a session cookie, then makes rapid API calls that are authenticated by the cookie rather than by SAML assertions directly. Because Rails controllers may skip rate limits for requests that pass cookie-based authentication, the API’s unauthenticated attack surface includes endpoints that are reachable after SAML login.

Another vector is the SAML Single Logout (SLO) flow. If logout requests or artifact resolution endpoints are not rate-limited, an attacker can generate many logout or binding requests to degrade service or trigger excessive processing. Because SAML endpoints often accept unsigned or lightly validated requests in certain configurations, Rails routes like /saml/consume or /saml/logout can be hammered without per-identity throttling.

Additionally, when Rails apps rely on the SAML response’s NameID or attributes for authorization, missing mapping to a stable rate-limit key can cause the app to fall back to IP-based limits, which are easily bypassed via proxies or NAT. Without tying rate limits to the authenticated identity (e.g., NameID or a mapped user ID), repeated SAML-authenticated sessions from the same user can exceed intended quotas.

The interplay of SAML’s redirect-based flow, Rails’ default route handling, and inconsistent application of rate limits to SAML-bound identities expands the attack surface. Findings for Rate Limiting in middleBrick scans often highlight missing limits on SAML consumer routes and session endpoints, exposing identity-based endpoints to enumeration and brute-force patterns that would be blocked if limits were enforced on authenticated user identifiers.

Saml-Specific Remediation in Rails — concrete code fixes

Apply rate limits to SAML-specific routes and tie them to the authenticated identity rather than IP. Use a stable identifier from the SAML assertion (such as NameID or a mapped user ID) as the rate-limit key. Below are concrete patterns and code examples for Rails.

1. Rate-limit the SAML consumer endpoint by NameID

In your SAML consumer controller, extract the NameID and use it with a throttling gem like rack-attack.

class SamlController < ApplicationController
  skip_before_action :verify_authenticity_token, only: [:consume]

  def consume
    response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings: saml_settings)
    if response.success?
      nameid = response.name_id        # e.g. "[email protected]"
      if rate_limited_by_nameid?(nameid)
        render json: { error: 'rate limit exceeded' }, status: 429
      else
        # your sign-in logic
        render json: { status: 'ok' }
      end
    else
      render json: { error: 'invalid assertion' }, status: 400
    end
  end

  private

  def rate_limited_by_nameid?(nameid)
    key = "saml:#{nameid}"
    begin
      # Using redis-rb as an example; adapt to your store
      hits = $redis.incr(key)
      $redis.expire(key, 60) if hits == 1
      hits > 10  # limit to 10 requests per minute per NameID
    rescue
      false      # fail open to avoid blocking auth during store issues
    end
  end
end

2. Protect SAML Single Logout (SLO) and artifact resolution

Rate-limit logout and artifact endpoints by the NameID or session ID to prevent DoS via repeated logout requests.

class Saml::LogoutController < ApplicationController
  skip_before_action :verify_authenticity_token, only: [:slo]

  def slo
    nameid = params[:NameID] || session[:nameid]
    if nameid && rate_limited_by_nameid?(nameid)
      logger.warn("Rate limit on SLO for #{nameid}")
    end
    # process SAML logout request
    head 200
  end

  private

  def rate_limited_by_nameid?(nameid)
    key = "saml_logout:#{nameid}"
    # Allow at most 5 logout requests per minute per identity
    hits = $redis.incr(key)
    $redis.expire(key, 60) if hits == 1
    hits > 5
  rescue
    false
  end
end

3. Map SAML attributes to a user and apply authenticated rate limits in API controllers

In your API controllers, ensure that authenticated endpoints use the mapped user ID rather than IP for rate limiting.

class Api::V1::ProfilesController < ApplicationController
  before_action :authenticate_from_saml_session
  before_action :throttle_by_user_id, only: [:show, :update]

  def show
    render json: { profile: current_user.profile }
  end

  private

  def authenticate_from_saml_session
    # Assuming you stored NameID in session after SAML login
    nameid = session[:nameid]
    @current_user = User.find_by(saml_nameid: nameid)
    head 401 unless @current_user
  end

  def throttle_by_user_id
    key = "api_user:#{@current_user.id}"
    hits = $redis.incr(key)
    $redis.expire(key, 60) if hits == 1
    if hits > 100  # 100 requests/minute per authenticated user
      render json: { error: 'rate limit exceeded' }, status: 429
    end
  rescue
    false
  end
end

4. Use consistent SAML settings and OneLogin RubySAML configuration

Ensure your SAML settings are centralized and that NameID is preserved for mapping. Example config/initializers/saml.rb:

SAML_SETTINGS = {
  issuer: 'https://app.example.com/saml/metadata',
  assertion_consumer_service_url: 'https://app.example.com/saml/consume',
  idp_sso_target_url: 'https://idp.example.com/sso',
  idp_sso_logout_url: 'https://idp.example.com/slo',
  cert_fingerprint: 'AB:CD:EF:...',
  name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
  security: {
    authn_requests_signed: false,
    logout_requests_signed: false,
    logout_responses_signed: false,
    embed_sign: false,
    want_assertions_signed: true,
    want_response_signed: false
  }
}.freeze

Frequently Asked Questions

How can I verify that rate limits are applied to SAML endpoints in a Rails app?
Use a middleBrick scan against your SAML endpoints (e.g., /saml/consume, /saml/logout). The report will flag missing rate limiting on authenticated identity paths and suggest tying limits to NameID or mapped user IDs rather than IP.
Does SAML session reuse require fresh rate-limit checks on each API call?
Yes. After SAML login, each API request authenticated by a session cookie should still be subject to rate limits keyed to the SAML identity (e.g., NameID). Do not rely on IP-based limits; map the identity to a stable key and enforce limits per identity in API controllers.