tags:

views:

182

answers:

3

I want to prevent users from accidentally posting a comment twice. I use the PRG (post redirect get) method, so that I insert the data on another page then redirect the user back to the page which shows the comment. This allows users to refresh as many times as they want. However this doesn't work when the user goes back and clicks submit again or when they click submit 100 times really fast. I don't want 100 of the same comments.

I looked at related questions on SO and found that a token is best. But I am having trouble using it.

//makerandomtoken(20) returns a random 20 length char. 

<form  method="post" ... >
<input type="text" id="comments" name="comments" class="commentbox" /><br/>
<input type="hidden" name="_token" value="<?php echo $token=makerandomtoken(20); ?>" />
<input type="submit" value="submit" name="submit"  />
</form>

if (isset($_POST['submit']) && !empty($comments)) 
{
    $comments= mysqli_real_escape_string($dbc,trim($_POST['comments']));

    //how do I make the if-statment to check if the token has been already set once?
    if ( ____________){ 
        //don't insert comment because already clicked submit
    }
    else{
        //insert the comment into the database
    }
}

So I have the token as a hidden value, but how do I use that to prevent multiple clicking of submit.

METHODS: someone suggested using sessions. I would set the random token to $_SESSION['_token'] and check if that session token is equal to the $_POST['_token'], but how do I do that? When I tried, it still doesn't check

A: 

I would insert a blank record into my table using that random key as the PK, then update against that PK so if anything they would be updating that PK many times. However, that has potential to be abused, so maybe only update if the "message body" and "title field" fields are both empty... Or use a staging field with the same logic, and only update where the staging bit is empty.

What have you considered as an approach to conquer this particular problem?

drachenstern
i considered inserting a unique field in the table and then having a check to see if that unique field exists; if it does=not insert, if not=insert. however the problem is getting that unique field. It's easy for signing up users because one unique field would be their username. But for comments on a post, have any suggestions as to a unique field?
jpjp
I like what you have already. Don't you have a unique field on each comment record? Can you had a unique indexed field on the comment table if you don't already have one, and use that as your uniqueness key. I would only insert where you're generating the UUID for the comment. Otherwise only update. Does this make sense?
drachenstern
Downvote. You end up inserting a row every time a comment form is displayed, which on many sites is on every page hit. Ouch!
tc.
@TC, what the hell man, I said only insert when you generate the random ID. Meaning, only when you present the form to add a comment. Granted, it does allow for abuse, but not for the reason you're thinking of. Only because they can spawn a lot of new comment requests.
drachenstern
A: 

I'd skip the whole random token thing and just store (a hash of) the comment in the session. If it matches the existing value stored in the session, then drop the comment. If not, let it through. Obviously there are problems:

  • It stops the user posting the same comment in different places. If this is a problem, store a hash of the comment and the id of the thing it's commenting on.
  • The user can press "back" and submit a different comment. I consider this a feature (it's better than dropping the comment because it matches the random token of a previous comment). Automatically interpreting this as a comment-edit is not easy.
  • It doesn't work if a user presses "submit" in alternate tabs. (I consider this unlikely, and it doesn't require the storage of multiple random tokens.)

You might want a random token to prevent XSRF anyway, but that's another issue (and in that case, you want to make sure that the random token is the same as "what it should be"; I'd store a long-lived one in the session).

Also, consider using prepared statements.

tc.
+3  A: 

If you want to prevent double submissions, you must store the state of "is submitted" versus "is not submitted". You have several options for where to keep this information.

  • Database - Add an hidden field with an autogenerated value that is unique (you can generate a short random string it and append the current time). This value can also be used to identify the conversation -- if you need a stateful web conversation. Add this value to the database and make it unique. Disadvantages: redundant storage in the database, reduced performance on comment insert, have to generate a unique string.
  • Session - Add the same hidden field with a value generated in a similar matter. When the user submits the form, save the value in the session if it's not there already. If it is, it's a double submission. Disadvantages: you still need to generate the unique token.
  • Browser - (1) Add some javascript to disable the submit button once it's clicked. (2) have an hidden field that starts with the value 0 and is changed to 1 when the user clicks the submit button. If the user clicks the button again, you check whether the value is 1 and abort if it is. Advantages: no unique string. Disadvantages: requires javascript to be enabled; you might require the string anyway to implement stateful web conversations.
Artefacto
thanks, i'll try this out and see what i get!
jpjp