Handling user access and Ajax

Events happening in the community are now at Drupal community events on www.drupal.org.
andreashaugstrup's picture

How does one handle access control when doing Ajax requests? Take a completely hypothetical example: I have my Drupal install setup in a way where only authenticated users may attach comments. I also want to add comments using an Ajax call instead of a normal fom submission (I said this was hypothetical, not that it was practical).

How would I make this secure? The comments box is stripped automatically for anonymous users, but anyone who knows where the Ajax call submits to will be able to send along the usual arguments. How do I make sure it was a validated user who submitted?

I have read some general articles on security and Ajax. I know enough to know that it's Really Important to get right, but not enough to sit down and write an implementation (I'm just the guy who can assist in writing some documentation). It would however be immensely helpful if a general .js existed that could handle security when doing Ajax requests through jQuery.

Comments

You just define a normal

Frando's picture

You just define a normal menu path for your ajax call.
So in hook_menu, you have for example

<?php
items
[] = array(
     
'access'             => user_access('post comments'),
     
'callback'           => 'mymodule_ajax',
     
'path'               => 'mymodule/ajaxrequest',
     
'title'              => t('Test page'),
    );
?>

and then you just do your JS http request calls to example.com/mymodule/ajaxrequest. Access control is handled by the menu system and the access parameter of the menu item's array. In the specified callback function, you do something like this
<?php
function mymodule_ajax() {
/* do whatever you want here, output anything you want to have returned directly via echo */
exit();
}
?>

regards,
frando

and if you meant to talk about client-side security control (something that's implented in javascript) - that can never be secure, as it's easy (and totally valid) to change this js on the client-side or to tell your browser to use other or new javascript (think of firebug as a tool to do this).

Maybe it would be better to

Frando's picture

Maybe it would be better to move the access control to the callback function, because with this scenario, you'd get a whole 403 error page returned if the user doesn't have access to it, which you probably don't like.

so you'd then do 'access' => true in the menu definition, and in your callback function, you do something like if (user_access('post comments')) echo $post_form; else echo $login_form; exit();

Ehhh, you don't generally

ChrisKennedy's picture

Ehhh, you don't generally need to work about sending weird 403 errors. It is safest to include the access check in the hook_menu definition where possible. As long as the UI elements are only displayed for users with the correct permission there will be no 403 errors unless someone tries to manually submit the Ajax request.

Although now that I think about it there are very rare cases where permission checking within the callback rather than menu is preferable. If the user's session expires after pageload but before the ajax request, or if a user is blocked after pageload and before ajax, you could theoretically want to handle the 403 error gracefully. Most people wouldn't worry about such weird situations though.

You're right, user access is key

nedjo's picture

User access issues are probably the most important concern when exposing data through AJAX. It's easy to make things available--much harder to ensure that they're only available when and to whom they should be.

Some basic suggestions:

  • Whenever possible, reuse existing access controls. E.g., see if you can post to the original form post url rather than exposing a new one.
  • Restrict any new menu paths you open by access (either the path or the callback, as others have suggested).
  • If attaching behaviours to a series of items, check their permissions individually as needed. E.g., load an array of nodes and then check each of them for node_access('edit', $node) to determine if the behaviour should be attached.
  • Wrap your queries in db_rewrite_sql() as appropriate, so that access limitations are respected. E.g., if you are loading an array of nodes.
  • If needed, explicitly check the path access before loading content. For a somewhat complex example, see function dynamicload_js() in dynamicload.module.

dynamicload!

mfredrickson's picture

Oh crap! I totally thought I invented that idea this week. Shucks!

http://cvs.drupal.org/viewcvs/drupal/contributions/modules/jquery47/page...

Now I have to get up off my laurels and go write it as a patch towards getting dynamicload D5 ready. Arg.

-M

A patch would be great

nedjo's picture

And even better if you want to be a maintainer of the module.

Like most of what I wrote for JS tools, it's a prototype waiting for further development.

I haven't decided what the best way to upgrade JS tools is. I wrote the various modules as depending on jstools.module because I kept finding I needed the same settings and methods and didn't want to repeat them. But it's awkward to have to download several modules if all you really want is one.

I'm a bit strapped for time but hoping to give the updates some energy in the next few weeks.

Javascript

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds:

Hot content this week