如何信任关联 ID 参数?
How to trust an association id parameter?
这是一个我们需要相信 conversation_id
没有被用户更改的例子:
# messages_controller.rb
def create
@message = Message.new(
body: message_params[:body], # trustworthy
user_id: current_user.id, # trustworthy
conversation_id: message_params[:conversation_id] # not trustworthy!
)
@message.save
end
所以我考虑将上面的代码包装在一个 if
语句中,像这样
# messages_controller.rb
def create
if current_user.conversations.pluck(:id).include? message_params[:conversation_id]
@message = Message.new(
body: message_params[:body],
user_id: current_user.id,
conversation_id: message_params[:conversation_id]
)
@message.save
end
end
这是我能想到的确保对话实际上是用户所属的唯一方法(未能仔细检查可能会导致恶意用户成功将消息写入 other人们的谈话!)
由于这种类型的检查一定很常见,我只想知道我是否有效地完成了它,或者是否有更好的方法或更多'rails way'?
我还应该补充一点,当消息不属于涉及用户的对话时,我有 cancan 保护 create 方法(这应该完全防止恶作剧本身)并且我在 conversation_id(我知道这不是真正的保护,但它都有帮助)。但我仍然想知道如何在没有这些保护的情况下做到这一点以增加深度。
rails 方法是使用嵌套路由,而不是通过请求正文传递 conversation_id
:
resources :conversations do
resources :messages, shallow: true
end
class MessagesController < ApplicationController
# POST /conversations/:conversation_id/messages
def create
@conversation = current_user.conversations
.find(params[:conversation_id])
@message = @conversation.messages.new(message_params) do |m|
m.user = current_user
end
if @message.save
redirect_to @conversation
else
render :new
end
end
private
def message_parameters
params.require(:message)
.permit(:body)
end
end
这可行 - 但它远非完美。如果 @conversation = current_user.conversations.find(params[:conversation_id])
没有找到记录,我们会收到 ActiveRecord::RecordNotFound
异常和 404 响应,而不是检查用户是否有权 post 进行该对话。
更好的解决方案是使用类似的东西:
@conversation = Conversation.find(params[:conversation_id])
unless conversations.users.exist?(id: current_user.id)
raise SomeKindOfAuthenticationError
end
当然你真的应该使用像 Pundit 这样的东西,而不是在这里重新发明轮子。
这是一个我们需要相信 conversation_id
没有被用户更改的例子:
# messages_controller.rb
def create
@message = Message.new(
body: message_params[:body], # trustworthy
user_id: current_user.id, # trustworthy
conversation_id: message_params[:conversation_id] # not trustworthy!
)
@message.save
end
所以我考虑将上面的代码包装在一个 if
语句中,像这样
# messages_controller.rb
def create
if current_user.conversations.pluck(:id).include? message_params[:conversation_id]
@message = Message.new(
body: message_params[:body],
user_id: current_user.id,
conversation_id: message_params[:conversation_id]
)
@message.save
end
end
这是我能想到的确保对话实际上是用户所属的唯一方法(未能仔细检查可能会导致恶意用户成功将消息写入 other人们的谈话!)
由于这种类型的检查一定很常见,我只想知道我是否有效地完成了它,或者是否有更好的方法或更多'rails way'?
我还应该补充一点,当消息不属于涉及用户的对话时,我有 cancan 保护 create 方法(这应该完全防止恶作剧本身)并且我在 conversation_id(我知道这不是真正的保护,但它都有帮助)。但我仍然想知道如何在没有这些保护的情况下做到这一点以增加深度。
rails 方法是使用嵌套路由,而不是通过请求正文传递 conversation_id
:
resources :conversations do
resources :messages, shallow: true
end
class MessagesController < ApplicationController
# POST /conversations/:conversation_id/messages
def create
@conversation = current_user.conversations
.find(params[:conversation_id])
@message = @conversation.messages.new(message_params) do |m|
m.user = current_user
end
if @message.save
redirect_to @conversation
else
render :new
end
end
private
def message_parameters
params.require(:message)
.permit(:body)
end
end
这可行 - 但它远非完美。如果 @conversation = current_user.conversations.find(params[:conversation_id])
没有找到记录,我们会收到 ActiveRecord::RecordNotFound
异常和 404 响应,而不是检查用户是否有权 post 进行该对话。
更好的解决方案是使用类似的东西:
@conversation = Conversation.find(params[:conversation_id])
unless conversations.users.exist?(id: current_user.id)
raise SomeKindOfAuthenticationError
end
当然你真的应该使用像 Pundit 这样的东西,而不是在这里重新发明轮子。