Insecure Deserialization in Rails
How Insecure Deserialization Manifests in Rails
Insecure deserialization in Ruby on Rails typically arises when an application reconstructs objects from untrusted data, such as parameters, cookies, or HTTP payloads, without validating or sanitizing the content. Ruby’s Marshal module is frequently involved because it supports rich object graphs, but this capability is also the root of the risk. Attackers can craft serialized payloads that execute code when deserialized, leading to Remote Code Execution (RCE). Common Rails-specific code paths include ActionDispatch::Cookies signed cookies that store serialized objects, ActiveJob arguments serialized with Marshal, and parameters parsed by custom middleware or legacy integrations that call Marshal.load on user-controlled input.
Real-world attack patterns in Rails include tampering with signed cookies that contain serialized objects. For example, a session store might serialize user roles into a cookie. If the application uses Marshal to deserialize these values, an attacker who can forge or modify the cookie may execute arbitrary code. Another pattern is unsafe deserialization in webhook handlers or background jobs, where YAML or raw Marshal data is accepted from external sources. CVE-2015-3225 illustrates this class of issue in Rails itself, where unsafe YAML deserialization allowed attackers to execute code. In Rails applications, developers may inadvertently introduce the risk by using ActiveSupport::MessageVerifier with weak secret management or by bypassing signed streams with crafted data.
Rails-specific frameworks and libraries can also introduce deserialization risks. For instance, some older plugins or components might use Rubyzip entries that are deserialized without strict type checks, or background job processors that deserialize job arguments using Marshal. Even when using JSON for parameter parsing, custom deserializers that fall back to YAML or Marshal for certain types can open the door to injection attacks. The key is that any deserialization of data originating from outside the trusted boundary—such as HTTP requests, headers, or uploaded files—must be treated as hostile.
Rails-Specific Detection
Detecting insecure deserialization in Rails requires a combination of code review, runtime testing, and scanning with tools that understand Rails-specific patterns. During a code review, look for uses of Marshal.load, YAML.safe_load without strict whitelisting, or ActiveSupport::MessageVerifier and MessageEncryptor with hardcoded or poorly managed secrets. Inspect background job arguments, cookie serialization, and any custom middleware that processes raw request bodies. Pay special attention to code that processes uploaded files or parses webhook payloads, as these are common entry points.
Runtime detection can be performed using black-box scanning tools like middleBrick. middleBrick scans unauthenticated attack surfaces and includes checks aligned with the OWASP API Top 10, including unsafe consumption and input validation that can lead to deserialization risks. When you submit a Rails API endpoint to middleBrick, it runs targeted probes to detect whether the API accepts serialized data and whether deserialization occurs in a dangerous context. The scanner cross-references OpenAPI/Swagger specifications—resolving $ref references—with runtime behavior to highlight mismatches, such as endpoints that accept complex nested objects without proper validation or type constraints. middleBrick also integrates with CI/CD as a GitHub Action, which can fail builds if security scores drop below your defined threshold, helping catch deserialization risks before deployment.
To illustrate a detection scenario, consider a Rails controller that accepts JSON and passes it to a custom deserializer:
class PaymentsController < ApplicationController
def create
data = params[:payload]
object = CustomDeserializer.unsafe_load(data)
head :ok
end
end
If CustomDeserializer.unsafe_load internally uses YAML or Marshal, this endpoint could be vulnerable. middleBrick would flag this pattern during scanning by analyzing input handling and checking for risky deserialization paths.
Rails-Specific Remediation
Remediating insecure deserialization in Rails focuses on avoiding dangerous deserialization methods and using Rails-native, safe constructs. Prefer strong parameter filtering and type validation over generic deserialization. When you must handle serialized data, use formats like JSON with strict schema validation, and avoid Marshal and unsafe YAML loading. Rails provides safer alternatives such as ActiveModel::Serializers and parameter sanitizers that work within the framework’s conventions.
For signed cookies and messages, rely on ActiveSupport::MessageVerifier with a secure, rotated secret and prefer signed cookies over deserializing complex objects. If you must verify or encrypt data, use ActiveSupport::MessageEncryptor and ensure secrets are managed via Rails credentials or environment variables. Here is an example of safe verification without deserialization:
class SecureCookieService
def self.verify(data)
verifier = ActiveSupport::MessageVerifier.new(Rails.application.credentials.secret_key_base)
verifier.verify(data)
rescue ActiveSupport::MessageVerifier::InvalidSignature
nil
end
end
For background jobs, avoid passing raw serialized objects as arguments. Instead, pass IDs and re-fetch records from the database. If you must pass complex data, use JSON with a schema and validate on arrival:
class OrderJob < ApplicationJob queue_as :default def perform(order_id, metadata_json) metadata = JSON.parse(metadata_json, symbolize_names: true) # Validate metadata against a schema or strict keys OrderProcessingService.new(order_id, metadata).call end endAdditionally, audit third-party libraries for unsafe deserialization. Use tools like bundler-audit and Brakeman to detect vulnerable dependencies and keep Rails and gems updated. By combining input validation, strict schema checks, and avoiding
Marshaland unrestricted YAML, you can significantly reduce the risk of deserialization attacks in Rails applications.