Allowing Rails Strong Params to Have Multiple Types
Security is a big, and often overlooked part of typical web development. While many frameworks claim to have security baked in, there are still considerations for ensuring security in your app.
An example for Ruby on Rails is protected params. In any Ruby on Rails controller we have access to an object called params. This is an object that the client can store items in and the server can access during any controller action. This can be used for situations such as storing a user id for looking up profiles in GET requests. Or, it can be used to specify fields for a body in a POST request.
The default object will accept anything a client wants to send to our controller. This is not optimal as allowing full control can lead some to send malicious values being passed to us. To prevent this, Rails has a construct called strong parameters. This is a way to dictate what can be sent to the controller by a client. Anything not explicitly specified in the strong params will not be included in the controller’s params object.
Strong Params in Practice
Recently, I was building a controller that accepted links to create records of images for a gallery. The POST requests from the client could include either a single link to the image, or a collection of links for multiple images. I knew that I would need to be able to access an array of links, so this is how I defined my strong params in my controller:
As we can see above, I create a new wrapper, image_upload_params, to access the params object. I append the permit method onto params to allow specifying approved values. Inside permit I allow two items: a user_id which can be an integer with the user’s id, and an image_url value that allows an array of links. An important thing to remember when working with params.permit is that the array must always be declared as the last argument otherwise you will get a syntax error.
I tried out my app after modifying the params object and everything worked great when I passed in a collection. However, whenever I tried to pass a single link, all that was returned in my image_upload_params object was the user_id. Why was this?
Allowing Multiple Strong Params Types
The single link was not being included in my params object because the strong parameters were correctly removing an undeclared value. Above we defined our params to permit image_url values that are arrays. This does not specify single item links which are strings. Therefore, in order to be safe Rails will strip this value out of the params object before passing it to the controller actions.
How do we include both single and collection links? There are a couple ways we could do this. The first, but less elegant way, is to check the type of the argument and serve the correct params object accordingly:
Yes, this will work, but it is not very pretty or scalable. What would happen if we have three items that mix and match their typings? This could lead to up to eight different combination checks. This would be a headache for writing and maintaining.
A cleaner approach would be to do inline declarations within the same permit method. We can do this by explicitly defining both single and collections as possible params arguments. This is my updated code:
Here we can see that the params now permits both strings and arrays as possible arguments for our image_url params value. Again, the order matters for placing the array at the end to avoid syntax errors.
The extra work of making your app more secure often leads to a better understanding of the framework and better software in general. Don’t let security be an after thought in order to have easier to implement code. I hope that this article has helped you better understand strong params in Rails and how to work with multi type param values!