Filter Rails JSON input with route constraints

Following the recent YAML parsing vulnerabilities in Rails, I decided to act on an idea I had a few months ago: using route constraints to define strict API contracts in Rails.

Sadly, it does not protect against the YAML parsing problem (and the probable similar vulnerabilities we will see in the following months), because the request is interpreted before going through the route constraints. But it can protect from the mass assignment vulnerability., and probably some SQL injections.

Here is the idea: Rails 3 introduced the route constraints, a way to execute fonctions on the request before it is passed to the controllers. By combining it with the json-schema gem, we can filter the JSON input quite easily.

For the following example data:

[sourcecode language="javascript"]
{"echo" : "blah", "nb" : 1, "data": [1, 2, 3]}
[/sourcecode]

We can define the following schema:

[sourcecode language="javascript"]
{
"type": "object",
"$schema": "http://json-schema.org/draft-03/schema",
"id": "#",
"required": false,
"additionalProperties": false,
"properties": {
"data": {
"type": "array",
"id": "data",
"required": false,
"items": {
"type": "number",
"id": "0",
"required": false
}
},
"echo": {
"type": "string",
"id": "echo",
"required": false
},
"nb": {
"type": "number",
"id": "nb",
"required": false
}
}
}
[/sourcecode]

(Use the JSON schema generator to create your own)

Save this schema to "data.schema" and add "json-schema" to your Gemfile. You will then be able to filter inputs with code like the following "config/routes.rb":

[sourcecode language="ruby"]
require "json-schema"
class LolJSONConstraint
def matches?(request)
if(request.headers["CONTENT_TYPE"] == "application/json")
JSON::Validator.validate("data.schema", request.headers["action_dispatch.request.request_parameters"])
end
end
end

Yamlvuln::Application.routes.draw do
resources :posts, :constraints => LolJSONConstraint.new
end
[/sourcecode]

The constraint will load the schema, and apply it to the incoming data, and return a 404 error if the JSON is invalid. The "additionalProperties" set to false in the schema is required to refuse the properties you didn't define and protect the application from mass assignment.

If I tried, for example, to send the following JSON to the application, there would be an error:

[sourcecode language="javascript"]
{"echo" : "blah", "nb" : "UNION ALL SELECT LOAD_FILE(CHAR(34,47,101,116,99,47,112,97,115,115,119,100,34))", "data": [1, 2, 3]}
[/sourcecode]

As I said before, it is not safe against the YAML parsing vulnerability. Also, I did not really test the performance of this. But it is still a nice and easy solution for API filtering.