views:

933

answers:

9

For my web application (PHP/MYSQL), I show a list of items and a link on each row to delete the item. Right now, the link is

<a href='item.php?id=3&action=delete'>Delete Item</a>

If I wanted to use POST instead... how would I do it (this is a dynamically generated list)? Can I send POST data without the use of a form?

Or, for every item, would I have to do:

<form action='item.php?id={$item_id}' method='POST'>
    <input type='hidden' name='action' value='delete'>
    <input type='submit' value='delete item'>
</form>

and style the submit button to look like the original link?

I am not familiar with php CURL or REST, would they help address this issue?

A: 

It’s still better to do it with GET.

If your problem is that you don’t like the ugly URL of your link, you should be able to fix that with mod_rewrite (if you use Apache web server).

Edit: There is no reason whatsoever to use POST here. No. Reason.

People around here write about safe and unsafe as if chosing method can influence safety in any way. Of course you should authenticate your user no matter what method you choose. If you don’t, then your software is already broken. If you use javascript to emulate sending a form when you don’t have a form, and don’t need it, don’t need any javascript at all, then your software is already broken.

Actually, about 90% of web software is broken, because people have no idea about what they are doing.

Ignore this comments’ being heavily minused by some strange people. Avoid javascript (no need), avoid POST (no reason), authenticate your user (safety), make the href beautiful with mod_rewrite or some other way (being nice).

Ilya Birman
Using GET opens the door to cross-site request forgery.
erickson
I believe using POST does not make the app invulnerable to cross-site request forgery
Abi Noda
CSRF is still possible with POST. However, the convention has been established that GET should be "safe" and not have side-effects: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Miles
It's not even about an ugly url, it's not a good idea for using GET when dealing with state.
patricksweeney
This is so wrong, I can't even begin to imagine how anyone would even come up with this ... Having a GET have side-effects is plain illegal per the HTTP specification. Period.
Jörg W Mittag
You guys have no idea what you are talking about.
Ilya Birman
Nice trollin', Ilya.
troelskn
-1: bad answer. The only useful point was to authenticate your users, but that is common sense and (unfortunately) not always possible
Adam Hawes
Crawling is not an issue since, as you note, this page should be requiring authentication. But GET/href should *only* be used for retrieving data and *never* for adding, updating or deleting data. Using POST avoids most CSRF issues.
pbreitenbach
+13  A: 

In general it's not a good idea to have a GET request that modifies the system state somehow, like deleting an item.

You could have your form look like this:

<form action='item.php' method='POST' id='form'>
    <input type='hidden' name='action' value='delete' />
    <input type='hidden' name='id' value='{item_id}' />
    <a href="" onclick="document.getElementById('form').submit(); return false;">Delete item</a>
</form>
Chris AtLee
This is the "best practice" approach for this use case -- a link that submits a form.
erickson
If you didn't want a ton of forms in your page, you could have one form and use a javascript function to set the value and submit the form, i.e. onclick="deleteItem('{item_id}'); and have the function set the value and call form.submit()
Peter Richards
thanks guys, this answers my question. I should be checking for cross-site request forgery and user authentication on @ item.php, correct? Are there any other security issues I should be checking for on the other side?
Abi Noda
Yes make sure you still have a valid user. Also, always check inputs from your users...all your inputs (so action as well as id). DON'T write a query that uses $_GET('id') directly, make sure the value is safe.
acrosman
It's not just not a good idea, it's plain illegal. The HTTP specification clearly states that a GET is not allowed to have any side-effects.
Jörg W Mittag
Better than the link would be a submit button. Works without JavaScript, doesn't get confused when you middle-click it, and so on. If you want it to look like a link, you can restyle it with CSS so it doesn't look like a button.
bobince
Personally, I prefer link a to a confirmation page. This page has a form, which - when POST'ed - deletes the entry and redirects back to the list. If you want to leave the confirmation-page out, at least use a submit-button, rather than an <a> tag with javascript. Buttons shouldn't look like links.
troelskn
troelskn, your solution is the best. The solution given in this answer, however, is the worst.
Ilya Birman
Users without javascript wouldn't be able to perform a delete.
cherouvim
+8  A: 

You should never change anything in your database (other than logging information or other ephemeral data) from a GET request. The issue is that there is various web spidering software, web accelerators, anti-virus programs, and the like, that will perform a GET request on every URL they find; you would not want them to delete items automatically when they do so. GET is also vulnerable to cross-site request forgery; if an attacker makes one of your users click on a link that performs a bad action (for instance, creating a tinyurl that redirects to a delete URL), then they can trick the user into using their permissions to delete something without realizing it.

Yes, you will need a form that you submit to create a POST request. The other option is to use JavaScript and XMLHttpRequest, but that wont work for users who have JavaScript disabled.

You should also ensure that once you have accepted the data from the POST request, instead of returning a new page in response to that request, you should redirect the user to a page accessed by a GET request. This way, they will not accidentally re-send the POST request if they hit reload, or hit their back button later in their browsing session.

Brian Campbell
+10  A: 

Please use POST for anything that modifies persistent state in the database. You don't want crawlers visiting your delete links!
Have a read at Architecture of the World Wide Web, Volume One and URIs, Addressability, and the use of HTTP GET and POST by W3C.

cherouvim
Yep, and crawlers cannot no way do a POST, so using this broken, weird method of emulating POST through unneeded form and useless piece of javascript instead of proper authentication on server side will make this app secure! Oh...
Ilya Birman
@Ilya Birman: I'm not sure what you mean.
cherouvim
+3  A: 

You don't want to use Get because of the REST principle of not allowing Gets to change the state of the system. Unless you like search engines to delete all your content.

You won't need a form for each item; you can use one method=post form around the list with a delete_{id} input type=submit. Or, more cleverly, <input type=submit name="delete_item" value="{id}">. Names are allowed on submit buttons.

Per your question in the comments, <input type=submit name="action_{id}" value="Delete"> might work better, though it suffers from some issues that would work badly for localized sites. You can always revert to a HTML Button for a little more control over the presentation. It acts like a submit button by default.

The fact that you may get extra, unwanted information in the submission is mitigated by the average-case of sending back a much larger page than necessary with your proposed form-per-item solution, when you're just viewing the list. You can always use javascript to substitute the behavior of the plain-old-html form with a smarter version for javascript-capable clients.

If you're going to link to a "Get" for deletion, you should return a confirmation page with that Get that actually does a post upon confirmation.

JasonTrue
the problem with using a "input type=submit" in each row is that the value has to be the id... but I want the link to say "Delete", not show an id. Is there a way around this I am overlooking?
Abi Noda
This has nothing to do with REST. It's just plain illegal per the HTTP specification to have a GET have side-effects.
Jörg W Mittag
ah, good point.It's hacky, but perhaps:<input type="submit" name="action\_1" value="Delete">
JasonTrue
For me, REST is basically a formalization of following the HTTP Spec.
JasonTrue
Though, to be fair, REST doesn't necessarily strictly require using http.
JasonTrue
It's not an accident that REST and HTTP play well together. After all, both were created by Roy Fielding. But REST is a high-level architectural style for structuring large-scale information systems that span multiple organizations and last multiple decades. A RESTful system could just as well ...
Jörg W Mittag
... be implemented over FTP or even CORBA. OTOH, just because you use HTTP, doesn't mean you're using REST. In fact, just because you say you are using REST, doesn't mean you're using REST. Rails is a good example: Roy has explained multiple times why Rails is not RESTful and how to fix it, but ...
Jörg W Mittag
... they still claim RESTfulness.
Jörg W Mittag
A: 

As others have said, it's a really bad idea to use GET for destructive operations like delete, especially on Internet-facing web sites (or corporations with a Google Mini appliance) where web crawlers could accidentally delete all your data.

If you don't want to use a form, use an XMLHttpRequest to send the POST to your server. You could even set the method to DELETE if your server supports that.

If you can't use JavaScript and XHR (your users live in 1999), and you don't want to use a form in your list, use a link to a separate page where you can show the form and a probably a 'Are you sure?' message.

The best thing to do is probably a combination of the two options above: Render a link to a separate page with a form, but use JavaScript to rewrite the link as an XHR call. That way users from 1999 or 2009 can both have an optimal experience.

Will Harris
+2  A: 

Here's a good example of why not to use GET to change server state:

http://www.infoworld.com/article/08/06/16/25FE-stupid-users-part-3-admins_5.html

The key portion is:

"It logged into the administrative area and followed the 'delete' link for every entry," the admin says.

If the delete had been coded as a POST this never would have happened. (OTOH we'd be robbed of a funny sysadmin story.)

Chris Winters
+2  A: 

POST is not a protection from all malicious behavior as some people have implied. Malicious users can still create links (that contain javascript to do the POST) and cause the same cross-site scripting problems as with GET. (<a href="javascript:function () {...}"/>)

That said, all the other reasons for using POST over GET apply (crawlers and the like).

scotts
POST isn't protection from malicious behaviour. Nothing is. POST will protect you from very common behaviour (regular crawlers, web accelerators, etc) when software runs GET requests on every link it finds.
Adam Hawes
A: 

You should not use an href to delete an item.

I would suggest doing this the old fashioned way and implementing a form-post for each row/item.

For example:

<tr><td>Item 1</td><td><form action=/delete method=post><input type=hidden name=id value=1><input type=submit value=Delete></form></tr>
<tr><td>Item 2</td><td><form action=/delete method=post><input type=hidden name=id value=2><input type=submit value=Delete></form></tr>
<tr><td>Item 5</td><td><form action=/delete method=post><input type=hidden name=id value=5><input type=submit value=Delete></form></tr>

Two other options: 1) using one with for each item 2) Ajax (but you will need to be proficient in Ajax)

pbreitenbach