HIGH excessive data exposuregrape

Excessive Data Exposure in Grape

How Excessive Data Exposure Manifests in Grape

Excessive Data Exposure in Grape APIs occurs when endpoints return more data than necessary, exposing sensitive information to attackers. This vulnerability is particularly prevalent in Grape due to its DSL-based approach and the way developers structure their API responses.

The most common manifestation appears in route definitions where developers use present or expose without proper attribute filtering. Consider this typical Grape pattern:

class UsersAPI < Grape::API
  format :json
  
  get '/users/:id' do
    user = User.find(params[:id])
    present user, with: Entities::User
  end
end

The issue arises when the Entities::User entity exposes all attributes by default:

class Entities::User < Grape::Entity
  expose :id
  expose :name
  expose :email
  expose :phone
  expose :address
  expose :created_at
  expose :updated_at
  expose :password_digest
  expose :ssn
  expose :credit_card
end

Here, fields like password_digest, ssn, and credit_card should never be exposed to API consumers, yet they're readily available if an attacker discovers the endpoint.

Another Grape-specific pattern involves nested entities with recursive exposure. When entities reference each other without proper depth control, attackers can exploit this to extract more data than intended:

class Entities::User < Grape::Entity
  expose :id
  expose :name
  expose :email
  expose :orders, using: Entities::Order
end

class Entities::Order < Grape::Entity
  expose :id
  expose :total
  expose :user, using: Entities::User # RECURSIVE EXPOSURE
end

This creates a potential for infinite recursion or excessive data payloads that can be exploited for information gathering.

Grape's flexible parameter handling also contributes to this issue. Developers often use params[:all] or similar broad parameter access patterns:

get '/users' do
  users = User.where(params[:all])
  present users, with: Entities::User
end

This allows attackers to manipulate query parameters to access data they shouldn't see, such as filtering by role=admin to find administrative users.

Version-specific exposure is another Grape vulnerability. Developers might expose sensitive fields in newer API versions while forgetting to restrict them:

version 'v2', using: :header, vendor: 'myapp' do
  get '/users/:id' do
    user = User.find(params[:id])
    present user, with: Entities::UserV2 # EXPOSES SENSITIVE FIELDS
  end
end

Without proper access controls, version v2 might expose fields that were restricted in v1, creating a security regression.

Grape-Specific Detection

Detecting Excessive Data Exposure in Grape APIs requires both static code analysis and runtime scanning. middleBrick's approach combines these methods to identify vulnerabilities specific to Grape's architecture.

For static detection, middleBrick analyzes Grape entity definitions and route configurations. It looks for patterns like:

# Suspicious entity definition
class Entities::User < Grape::Entity
  expose :id
  expose :name
  expose :email
  expose :password_digest # FLAGGED: Sensitive field exposure
  expose :ssn # FLAGGED: PII exposure
end

The scanner identifies entities that expose fields containing keywords like password, secret, token, key, ssn, credit, ssn, dob, address, and similar sensitive terms.

Runtime detection involves scanning actual API responses to verify what data is being returned. middleBrick's black-box scanning tests endpoints and analyzes the JSON structure:

{
  "id": 123,
  "name": "John Doe",
  "email": "john@example.com",
  "password_digest": "$2a$12$Kvi...",
  "ssn": "123-45-6789",
  "credit_card": "4111111111111111"
}

The scanner flags responses containing sensitive data fields, even if they're not documented in the OpenAPI spec.

middleBrick also tests for parameter manipulation vulnerabilities specific to Grape:

# Test for parameter-based data exposure
curl -X GET "https://api.example.com/users?role=admin&status=active"

If the endpoint returns administrative users or sensitive data based on these parameters, it indicates a vulnerability.

For entity recursion detection, middleBrick analyzes the entity graph to identify circular references and excessive nesting depth:

# Detected recursive exposure
class Entities::User < Grape::Entity
  expose :id
  expose :name
  expose :orders, using: Entities::Order
end

class Entities::Order < Grape::Entity
  expose :id
  expose :total
  expose :user, using: Entities::User # RECURSIVE
end

The scanner calculates the potential response size and flags configurations that could lead to excessive data exposure or denial-of-service through large responses.

Version-specific exposure testing verifies that newer API versions don't inadvertently expose more data than intended:

# Test different API versions
curl -H "Accept: application/vnd.myapp-v2+json" \
     https://api.example.com/users/123

middleBrick compares responses across versions to identify data exposure regressions.

Grape-Specific Remediation

Remediating Excessive Data Exposure in Grape requires a multi-layered approach using Grape's built-in features and Ruby best practices. The most effective strategy combines attribute whitelisting, role-based exposure, and careful entity design.

Start with strict attribute whitelisting in your entities. Never expose all attributes by default:

class Entities::User < Grape::Entity
  # ONLY expose what's absolutely necessary
  expose :id, documentation: { type: 'Integer', desc: 'User ID' }
  expose :name, documentation: { type: 'String', desc: 'Full name' }
  
  # CONDITIONAL exposure based on context
  expose :email, if: { type: :public } do |user, opts|
    user.email if opts[:current_user]&.admin?
  end
  
  # BLOCK exposure for computed fields
  expose :full_address, if: { type: :admin } do |user, opts|
    next unless opts[:current_user]&.admin?
    [user.address, user.city, user.state, user.zip].compact.join(', ')
  end
  
  # EXCLUDE sensitive fields entirely
  # No exposure for: password_digest, ssn, credit_card, etc.
end

Use Grape's conditional exposure (if: option) to control data visibility based on user roles and request context. This prevents unauthorized access to sensitive information.

Implement proper parameter filtering to prevent data leakage through query manipulation:

class UsersAPI < Grape::API
  params do
    optional :role, type: String, values: ['user', 'admin']
    optional :status, type: String, values: ['active', 'inactive']
    # DO NOT allow arbitrary field filtering
  end
  
  get '/users' do
    # WHITELIST allowed filters
    allowed_filters = [:role, :status, :created_after]
    filtered_params = declared(params, include_missing: false)
    
    users = User.all
    users = users.where(filtered_params.slice(*allowed_filters))
    
    present users, with: Entities::User
  end
end

This approach prevents attackers from using parameters like password=1 or ssn=1 to manipulate queries.

For nested entities, implement depth control and proper exposure limits:

class Entities::User < Grape::Entity
  expose :id
  expose :name
  expose :email
  
  # LIMIT nested exposure
  expose :orders, using: Entities::Order, if: { type: :detailed } do |user, opts|
    next [] unless opts[:include_orders]
    user.orders.limit(10) # LIMIT result set
  end
end

class Entities::Order < Grape::Entity
  expose :id
  expose :total
  expose :created_at
  
  # DO NOT expose user reference to prevent recursion
  # expose :user, using: Entities::User # REMOVE this line
end

Break recursive relationships and use pagination or limits to control response size.

Implement version-specific entities with proper field restrictions:

class Entities::UserV1 < Grape::Entity
  expose :id
  expose :name
  expose :email
end

class Entities::UserV2 < Grape::Entity
  expose :id
  expose :name
  expose :email
  # NO additional sensitive fields exposed
end

Ensure newer versions don't accidentally expose more data than previous versions.

Use Grape's documentation features to explicitly mark sensitive fields and their exposure conditions:

class Entities::User < Grape::Entity
  expose :id, documentation: { type: 'Integer', desc: 'User ID' }
  expose :name, documentation: { type: 'String', desc: 'Full name' }
  
  expose :email, if: { type: :admin }, \
    documentation: { type: 'String', desc: 'Email address (admin only)' }
end

This makes the exposure rules explicit and helps prevent accidental data leakage.

Finally, implement comprehensive testing for data exposure:

require 'rspec'

describe Entities::User do
  let(:user) { create(:user, email: 'test@example.com') }
  
  it 'does not expose sensitive fields' do
    result = Entities::User.represent(user, type: :public)
    json = JSON.parse(result.to_json)
    
    expect(json.keys).to contain_exactly('id', 'name')
    expect(json).not_to have_key('email')
    expect(json).not_to have_key('password_digest')
    expect(json).not_to have_key('ssn')
  end
  
  it 'exposes email only to admins' do
    result = Entities::User.represent(user, type: :admin)
    json = JSON.parse(result.to_json)
    
    expect(json.keys).to include('email')
  end
end

Regular security scanning with middleBrick helps catch any regressions in data exposure controls.

Related CWEs: propertyAuthorization

CWE IDNameSeverity
CWE-915Mass Assignment HIGH

Frequently Asked Questions

How does middleBrick detect Excessive Data Exposure in Grape APIs?
middleBrick uses both static analysis of Grape entity definitions and dynamic runtime scanning. It analyzes entity classes for exposed sensitive fields, tests API endpoints to see what data is actually returned, and checks for parameter manipulation vulnerabilities. The scanner looks for patterns like exposed password fields, SSNs, credit card numbers, and recursive entity relationships that could lead to excessive data exposure.
Can middleBrick scan my Grape API if it's behind authentication?
Yes, middleBrick can scan authenticated Grape APIs. You can provide authentication credentials (API keys, JWT tokens, or basic auth) when submitting your API for scanning. The scanner will use these credentials to access protected endpoints and test the authenticated attack surface, ensuring comprehensive coverage of your API's security posture.