New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RB: Experimental strong params query #9696
base: main
Are you sure you want to change the base?
Conversation
…y1342/codeql into experimental-strong-params
QHelp previews: ruby/ql/src/experimental/weak-params/WeakParams.qhelpWeak or direct parameter references are usedDirectly checking request parameters without following a strong params pattern can lead to unintentional avenues for injection attacks. RecommendationInstead of manually checking parameters from the `param` object, it is recommended that you follow the strong parameters pattern established in Rails: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html In the strong parameters pattern, you are able to specify required and allowed parameters for each action called by your controller methods. This acts as an additional layer of data validation before being passed along to other areas of your application, such as the model. References
|
This seems like a good idea to me! You can simplify your query a lot, though. Firstly, there are many useful classes in To find calls to /**
* A call to `request` in an ActionController controller class.
* This probably refers to the incoming HTTP request object.
*/
class ActionControllerRequest extends DataFlow::Node {
ActionControllerRequest() {
exists(DataFlow::CallNode c |
c.asExpr().getExpr().getEnclosingModule() instanceof ActionControllerControllerClass and
c.getMethodName() = "request"
|
c.flowsTo(this)
)
}
} This doesn't include calls in ActionView classes unfortunately, but it's a start. The reason we use class MyController < ApplicationController
def create
r = request
User.create(r.request_parameters)
end
end Taking /**
* A direct parameters reference that happens inside a controller class.
*/
class WeakParams extends DataFlow::CallNode {
WeakParams() {
this.getReceiver() instanceof ActionControllerRequest and
this.getMethodName() =
[
"request_parameters"
]
}
} That gives us our source, now we need a sink. Luckily we already have modelling for ActiveRecord calls like class Configuration extends TaintTracking::Configuration {
Configuration() { this = "WeakParamsConfiguration" }
override predicate isSource(DataFlow::Node node) { node instanceof WeakParams }
override predicate isSink(DataFlow::Node node) { node = any(PersistentWriteAccess a).getValue() }
} In the above I haven't considered excluding uses inside "strong params" methods. This is for two reasons:
I'm also not sure where the |
The aim of this query to to find direct or bulk param accesses in controller methods that are outside of methods adhering to the strong parameter convention found in rails: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html By accessing parameters directly, it may lead to a vulnerability where an unexpected parameter key/value pair is included in a payload that is then mistakenly accessed or passed to other areas of the application, opening the application up to potential exploitation.
I have tried to add some DataFlow tracking to this query to help reduce false positives in cases where the raw parameter access data flows to a Model method of some kind, which appears to be the biggest category of vulnerabilities addressed by the strong parameters pattern in the Rails documentation. I'm curious if y'all think this is useful as written. I feel like it loses some fidelity when finding potential injection vulnerabilities where the sink is a Model method. It would be trivial to remove this, as the
WeakParams
class comprises of most of the query that pinpoints param accesses outside of strong param methods. I will also need to update the tests if y'all think the DataFlow component should stick around.