Scope it
For multi-tenant applications you should remember to scope associations.
Here’s an example to highlight potential scoping issues. Let’s consider the following simple domain model:
class Account < ApplicationRecord
has_many :categories
has_many :posts
# Defines an account (tenant).
# ...
end
class Category < ApplicationRecord
belongs_to :account
# An account has a predefined set of categories each post can have.
end
class Post < ApplicationRecord
belongs_to :account
belongs_to :author, class_name: "User"
belongs_to :category
# ...
end
When a user has to create post we will need to let the user choose the category from current_account.categories
- but it can easily be forgotten that the submitted field post[category_id]
should also be whitelisted at the model-level.
This problem can be solved in multiple ways.
1) Controller-level whitelisting
class PostsController < ApplicationController
def create
@post = current_account.posts.new(post_params)
@post.category = current_account.categories.find(@post.category_id) if @post.category_id
if @post.save
redirect_to root_url
else
render :new
end
end
private
def post_params
params.fetch(:post, {}).permit(
:title,
# + some other fields
:category_id,
)
end
end
2) Model-level whitelisting
The cleaner approach would be to move the white-listing to the model.
class Post < ApplicationRecord
before_validation :whitelist_category!
# ...
private
def whitelist_category!
self.category =
if account && category_id
account.categories.find(category_id)
end
end
end
Damage?
This vulnerability could in this specific example lead to one account showing the posts of another account if the posts are looked up via the category which is not a totally unreasonable thing you would do.
Conclusion
No matter the approach, we should remember to do this type of whitelisting when we let users set-up associations.