15

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?

GeekJock
  • 11,066
  • 13
  • 43
  • 44
  • 3
    Use GET if you want people/search engines delete stuff from your database just by visiting links, otherwise use POST. – alex Mar 24 '09 at 20:04
  • See http://stackoverflow.com/questions/46585/when-do-you-use-post-and-when-do-you-use-get My understanding is that when you are modifying the state, you should use POST. – GeekJock Mar 24 '09 at 20:04
  • Look, just as I said, what if this page is visited by web-crawler. It will just issue GET to every link on the page. If you are OK with it, then I am too. – alex Mar 24 '09 at 20:07
  • I understand that I should use POST in this situation, my question is what the best way of doing so is. Is there any way to do it without using an HTML form? – GeekJock Mar 24 '09 at 20:09
  • No, you need to use form or AJAX (JavaScript) to issue POST. – alex Mar 24 '09 at 20:13

10 Answers10

20

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.

Edit: Sometimes though you need to use GET. For example membership activation URLs which are sent in emails are GET and need to somehow modify the database.

cherouvim
  • 31,725
  • 15
  • 104
  • 153
  • 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 Mar 24 '09 at 21:26
18

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
  • 7,798
  • 3
  • 28
  • 27
  • 4
    This is the "best practice" approach for this use case -- a link that submits a form. – erickson Mar 24 '09 at 20:08
  • 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 Mar 24 '09 at 20:13
  • 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? – GeekJock Mar 24 '09 at 20:16
  • 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 Mar 24 '09 at 20:26
  • 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 Mar 24 '09 at 20:35
  • 3
    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 Mar 24 '09 at 20:35
  • 2
    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 tag with javascript. Buttons shouldn't look like links. – troelskn Mar 24 '09 at 21:43
  • troelskn, your solution is the best. The solution given in this answer, however, is the worst. – Ilya Birman Mar 25 '09 at 06:42
  • Users without javascript wouldn't be able to perform a delete. – cherouvim Apr 19 '09 at 05:41
8

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
  • 322,767
  • 57
  • 360
  • 340
3

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
  • 19,244
  • 4
  • 34
  • 61
  • 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? – GeekJock Mar 24 '09 at 20:31
  • 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 Mar 24 '09 at 20:36
  • ah, good point. It's hacky, but perhaps: – JasonTrue Mar 24 '09 at 20:38
  • For me, REST is basically a formalization of following the HTTP Spec. – JasonTrue Mar 24 '09 at 20:39
  • Though, to be fair, REST doesn't necessarily strictly require using http. – JasonTrue Mar 24 '09 at 20:39
  • 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 Mar 24 '09 at 21:04
  • ... 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 Mar 24 '09 at 21:06
  • ... they still claim RESTfulness. – Jörg W Mittag Mar 24 '09 at 21:12
3

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
  • 1,001
  • 1
  • 6
  • 5
3

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
  • 4,027
  • 3
  • 29
  • 26
  • 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 Mar 25 '09 at 01:19
0

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
  • 11,261
  • 3
  • 33
  • 24
0

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
  • 21,597
  • 12
  • 64
  • 64
0

you can also use get but you would need to check session values to ensure that it's the owner of the post who's attempting to delete. get is not "universally unsafe". it totally depends on how you use it.

Uğur Gümüşhan
  • 2,455
  • 4
  • 34
  • 62
-12

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
  • 9,834
  • 4
  • 27
  • 31
  • Using GET opens the door to cross-site request forgery. – erickson Mar 24 '09 at 20:07
  • I believe using POST does not make the app invulnerable to cross-site request forgery – GeekJock Mar 24 '09 at 20:12
  • 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 Mar 24 '09 at 20:17
  • 1
    It's not even about an ugly url, it's not a good idea for using GET when dealing with state. – patricksweeney Mar 24 '09 at 20:31
  • 4
    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 Mar 24 '09 at 20:37
  • You guys have no idea what you are talking about. – Ilya Birman Mar 24 '09 at 20:43
  • 1
    -1: bad answer. The only useful point was to authenticate your users, but that is common sense and (unfortunately) not always possible – Adam Hawes Mar 25 '09 at 01:20
  • 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 Jul 04 '09 at 06:54
  • 1
    @IlyaBirman More than 7 years later... do you _still_ think it's a good idea to do state modifications with GET? (Hint - please say no) – Krease Apr 14 '16 at 19:30