views:

42

answers:

3

Does using a hidden fields on a form allow for the possibility of someone spoofing the data in the hidden fields that are posted back?

For instance, in my Rials 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?

+1  A: 

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

PeterWong
It's even easier to spoof the project_id in the URL than it is in a hidden form field.
vonconrad
In most of the case, I don't mind. For example, a user goes to `/project/1/tasks` and filled in the form. Then he submitted to `/project/2/tasks` or `/project/abc/tasks`. So if no `params[:id]` or cannot find a project by the id (the `abc` case) or found another user's project (the `2` case), then I simply reject he. Otherwise create the task. Note that data sent by a form really cannot be trust, no matter using hidden field or embed in the url.
PeterWong
A: 

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.

Eimantas
Not a very good answer. It's even easier to spoof the project_id in the URL than it is in a hidden form field.
vonconrad
You don't need spoof protection when data filtering is done right.
Eimantas
+3  A: 

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.

vonconrad
Well, your last example looked good, but, I had problems with the route to "edit_project_task". I looked at "rake routes", and it's not there. I have resources connected properly and I do have a new_project_task route and it works. Thinking about it, specifying the @parent shouldn't be necessary to edit, since the @task will already have an id. Just edit_task_path(@task) should be enough. Besides that, create() actually adds a record in the db immediately. I don't like doing that. If the user reverts out, it will leave the record there, and you'd have to delete it.
MattSlay
Sure. But without pre-creating your task and adding it to your database, you'd have to reference the project_id somewhere on the form--usually through the URL or through a hidden form field--or you won't be able to associate the task to a project. And I thought that having the project_id in the URL or a hidden form field was exactly what you *didn't* want to do?
vonconrad