Command Injection in Grape with Bearer Tokens
Command Injection in Grape with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Command Injection occurs when an API passes untrusted input directly to a system shell or to a process builder without proper sanitization or validation. In Grape, a Ruby API framework, this often manifests in endpoints that invoke system commands via system, backticks, or Open3 methods, using data that should have been controlled or validated. When Bearer Tokens are used for authentication, developers may assume that because the request includes a token, the endpoint is safe from injection. This assumption is dangerous: authentication and input validation are independent concerns.
Consider a Grape endpoint that uses a token to identify a user but then passes an unchecked parameter to a shell command. For example, an endpoint might accept a filename intended to be processed by an external tool and embed it directly in a command string. Even if the request includes a valid Bearer Token, an authenticated or unauthenticated attacker who can control the parameter can inject additional shell commands. This is because the token does not constrain what the parameter can contain; it only attests identity.
In Grape, routes are defined as classes that inherit from Grape::API. If a developer writes an endpoint like the following, the stage is set for Command Injection:
class MyResource < Grape::API
format :json
desc 'Process a file', requires: [:token]
params do
requires :filename, type: String, desc: 'Name of the file to process'
requires :token, type: String, desc: 'Bearer token'
end
get :process do
# Dangerous: directly interpolating user input into a shell command
result = `process_file #{params[:filename]}`
{ output: result }
end
end
In this example, the Bearer token might be validated via an before block or an authentication helper, but the filename parameter is used verbatim in a backtick command. An attacker who obtains or guesses a valid token (or sends a request without token enforcement) can manipulate filename to execute arbitrary shell commands, such as ; ls -la or | cat /etc/passwd. The token does not mitigate this because the vulnerability lies in how the input is handled, not in whether the caller is authenticated.
Grape does not inherently sanitize external input. Developers must treat all parameters as untrusted, regardless of authentication method. The combination of Grape endpoints and Bearer Tokens can create a false sense of security, leading to overlooked input validation. This is especially risky when endpoints interact with system utilities, file operations, or external scripts.
Bearer Tokens-Specific Remediation in Grape — concrete code fixes
To prevent Command Injection in Grape when using Bearer Tokens, focus on strict input validation, avoiding shell interaction, and isolating authentication from execution logic. Below are concrete, safe patterns.
1. Avoid Shell Commands Entirely
The most reliable mitigation is to avoid invoking shell commands with user input. Use native Ruby libraries for file operations instead of shelling out.
class MyResource < Grape::API
format :json
helpers do
def authenticate!
# Example: validate Bearer token
token = request.env['HTTP_AUTHORIZATION']&.split(' ')&.last
error!('Unauthorized', 401) unless token == 'expected-token-value'
end
end
before { authenticate! }
desc 'List directory contents', requires: [:token]
get :list do
# Safe: use Ruby's Dir, no shell involved
{ entries: Dir.entries('/safe/path') }
end
end
2. Strict Allowlist Validation
If shell interaction is unavoidable, validate input against an allowlist of known-safe values.
class MyResource < Grape::API
format :json
params do
requires :action, type: String, values: ['start', 'stop', 'status']
requires :token, type: String
end
helpers do
def authenticate!
token = request.env['HTTP_AUTHORIZATION']&.split(' ')&.last
error!('Unauthorized', 401) unless token == 'expected-token-value'
end
end
before { authenticate! }
desc 'Execute a controlled action'
post :control do
# Safe: only predefined actions are allowed
case params[:action]
when 'start'
system('service myapp start')
when 'stop'
system('service myapp stop')
when 'status'
system('service myapp status')
end
{ result: 'ok' }
end
end
3. Use Open3 with Explicit Arguments
If you must execute external commands, use Open3 with an array of arguments to avoid shell interpretation.
require 'open3'
class MyResource < Grape::API
format :json
params do
requires :filename, type: String, regexp: /^[a-zA-Z0-9_.-]+$/ # strict regex
requires :token, type: String
end
helpers do
def authenticate!
token = request.env['HTTP_AUTHORIZATION']&.split(' ')&.last
error!('Unauthorized', 401) unless token == 'expected-token-value'
end
end
before { authenticate! }
desc 'Process a file safely'
post :process do
# Safe: arguments passed as array, no shell interpolation
stdout, stderr, status = Open3.capture3('process_file', params[:filename])
{ stdout: stdout, stderr: stderr, status: status.exitstatus }
end
end
4. Centralize Authentication and Logging
Ensure Bearer Token validation is centralized and that suspicious inputs are logged for review, without relying on the token to enforce input safety.
class MyResource < Grape::API
format :json
helpers do
def authenticate!
auth_header = request.env['HTTP_AUTHORIZATION']
unless auth_header&.start_with?('Bearer ') && valid_token?(auth_header.split(' ').last)
error!('Unauthorized', 401)
end
end
def valid_token?(token)
# Compare tokens securely, e.g., using ActiveSupport::SecurityUtils.secure_compare
token == 'expected-token-value'
end
end
before { authenticate! }
desc 'Safe endpoint with logging'
params do
requires :filename, type: String, regexp: /^[\w\-]+\.txt$/ # restrictive regex
end
post :upload do
# Log potentially malicious attempts
Rack::Logger.warn("Suspicious input: #{params[:filename]}") unless params[:filename] =~ /^safe_/
{ message: 'accepted' }
end
end
In all cases, the Bearer Token ensures the caller is authenticated, but input validation and safe execution practices are required to prevent Command Injection. middleBrick can help identify such risky endpoints during scans, providing findings tied to frameworks like OWASP API Top 10.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |