Does using a hidden fields on a form allow for the possibility of someone spoofing the data in the h开发者_JAVA百科idden fields that are posted back?
For instance, in my Rails app I have parent-child structure where I call new_project_task(@project) which hits the tasks controller create action, and on the page I do not want to show a field for the foreign key (project_id) because I do not want the user to see or edit it. However, if I leave the field off of the form, when the child record is saved, the project_id is not saved on the record. So, I'm using a hidden field to hold the foreign key value and it works properly during post backs to get that value into the child task record.
However, if you view the page source that is rendered in the browser, you can see the hidden field and the value, which maskes me wonder if this is a risky technique in case someone tried to monkey with the raw html content of before it was posted back. I don'y personally know how to hack web pages in this way, but I've heard it can be done.
So, is there a better Rails way of handling this?
The answer to your question comes down to what you're worried about. Do you want to prevent user mistakes, or do you want to prevent malicious use?
For the first one, having the project_id
in a hidden form field poses very little danger. Your users are unlikely to see it, and even more unlikely to care.
If you're trying to prevent malicious use, it becomes more problematic--but again, it depends on what you're trying to prevent.
If you're trying to prevent users from adding tasks to projects they don't have access too, use some sort of permission system to disallow the user to access certain projects. The easiest way for this is simply to have a relationship between users and projects, and then use current_user.projects.find(params[:project_id]).tasks.create
(or similar) in your controller.
Now, if you absolutely, positively, don't want users to add tasks to any other project--period-- you can consider pre-creating the task. Essentially, you'd create the task in your new
action and redirecting the user to the edit action. Something like this:
def new
@project = Project.find(1) # Or however you want to fetch the project
@task = @project.tasks.create
redirect_to edit_project_task(@project, @task) # May vary depending on your routes
end
With this approach, you'd want to remove your create
action completely.
In my practice, I would rather embed the project_id in the url (by using resourceful routes)
ie. /project/:project_id/task
In fact, I won't trust most of the things coming back from the form. (Consider someone built his own proxy server and thus could modify the form's data to anything else!)
So I would just confirm I have my things, then it's OK. I don't mind what the :project_id
came back, I just need to check if I could find the project by the :project_id
and the user has permission to handle the found project.
You could of course embed it in the hidden filed, it is valid (although it's not my personal preference). But it won't matter if the user "magically" changed the hidden filed to another value. You just insert it or refuse it, that's all :D
When you're using parent/child (project/task) you should use restful urls for their crud interface. When you're creating the task for the project, you post to the task creation url for that project specifically. The routes should look someting like this:
resources :projects do
resources :tasks
end
Then when you define new_project_task_path(@project)
you will get a url like /projects/1/tasks/new
where 1
will be the id of the project. Then in contorller action you should do this:
def create
Project.find(params[:project_id]).tasks.create(params[:task])
end
And all will be done for you. If you're trying to add a task to a project that belongs to specific user, do this:
def create
current_user.projects.find(params[:project_id]).tasks.create(params[:task])
end
To simplify code in actions you can define before_filter
which finds the project for you and puts it into @project
instance variable.
精彩评论