Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!


php security experts...submitting forms safely
New on LowEndTalk? Please Register and read our Community Rules.

All new Registrations are manually reviewed and approved, so a short delay after registration may occur before your account becomes active.

php security experts...submitting forms safely

raindog308raindog308 Administrator, Veteran
edited April 2013 in Help

Let's say I have a form that looks like this:

<form action="/script.php" method="post"> <input name="my_input" length="80" /> <input type="submit" value="submit" /> </form>

Now I also want to include a numeric identifier - call it a ticket id. "Here's the ticket history, do you want to add something?" The user can't modify that.

My question is...what is the safest way to get that ticket id in the form submission?

No problem accomplishing it, but my question is around security. So here are the ways to get a variable back that I can think of:

<form action="/script.php" method="post"> <input name="my_input" length="80" /> <input type="hidden" name="ticket_id" value="12345" /> <input type="submit" value="submit" /> </form>

or

<form action="/script.php?ticket_id=12345" method="post"> <input name="my_input" length="80" /> <input type="submit" value="submit" /> </form>

I'm concerned that someone could craft a malicious POST and submit it and append their comments to a different ticket. i.e., compose a POST from their own server/browser/tool. If I was doing this with GET then they certainly could do that just by changing the url vars - it's possible to do that also with POST too, right?

I can check that the user owns that ticket of course and do some other validation, but fundamentally, how do you present data to a user and safely get it back again in an HTML form?

Is there something other than creating a unique serial number ("FORM 12345 should present ticket id 6789") record on the server side and then checking it back?

I'm using PHP & MySQL on the backend though I'm not sure my question is specific to those technologies.

Comments

  • jarjar Patron Provider, Top Host, Veteran
  • BlueVMBlueVM Member
    edited April 2013

    cPHP actually has a framework built in which generates random hashes for variable names and decodes them on the other side. It's quite good :)+

    Anyway on the PHP side simply check that the currently logged in user owns the ticket being replied to before processing it any further. If the ticket id and the user id don't match die();

  • raindog308raindog308 Administrator, Veteran

    Yeah, I get the ticket # back and if the ticket isn't owned by the user, bail. And if the user does own the ticket, make sure he's not trying to force a reply to a closed ticket, etc.

    But I was wondering about this in general...how do you safely present data to a user and get it back? I'm thinking I have to do some kind of form-id-stamp and keep track of it on the server side.

  • joepie91joepie91 Member, Patron Provider
    edited April 2013

    @BlueVM said: Anyway on the PHP side simply check that the currently logged in user owns the ticket being replied to before processing it any further. If the ticket id and the user id don't match die();

    This. Just include the ticket ID in the form data or the URL, and check whether the user can actually post to that ticket, server-side. Anything client-side can be modified, so you shouldn't trust it.

    And you should indeed look into CSRF protection (which was what @BlueVM was refering to with regards to CPHP). This is a probably a good resource to read about this, or you could use a framework that provides CSRF protection by itself.

    @raindog308 said: But I was wondering about this in general...how do you safely present data to a user and get it back? I'm thinking I have to do some kind of form-id-stamp and keep track of it on the server side.

    You don't. At best you can store stuff in the session superglobal, but there's limited use for that.

    EDIT: And I should mention that it's a very bad idea to store anything related to a 'state' of a certain request, in the session superglobal. If you do something like saving the currently viewed ticket into a session variable, and the user loads two different tickets, then all replies on both pages will go to the ticket that was opened last. Don't do it.

  • @raindog308: You are describing a CSRF attack. So yes, one of the methods is to create a unique serial number/token that is sent in every form request and must match what you have on your end on post before processing.

    My other recommendation is to use a web framework stack. Really. Trust me, it saves you a lot of headache. The established ones usually will handle session/cookies, CSRF token generated, post validation and properly escapes the form data, that all you really need is to check that your user has access/is owner to the ticket, and voila, save the new data.

  • @raindog308 said: I can check that the user owns that ticket of course and do some other validation, but fundamentally, how do you present data to a user and safely get it back again in an HTML form?

    If it was me, I will always validate if user owns it. I want to treat server code as a separate entity, validate all things that were already validated in client side, and trust nobody.

    for your concern, how about a symmetrical encode/decode thing?

  • @raindog308 said: But I was wondering about this in general...how do you safely present data to a user and get it back? I'm thinking I have to do some kind of form-id-stamp and keep track of it on the server side.

    My suggestion, keep your original logic the same, to keep things simple. And attack this using filters or interceptors, something like that. so that its a separate code that handles this, and not your business logic code.

  • Let me share my experience here. This is what I have done.

    <form action="/script.php" method="post">
        <input name="my_input" length="80" />
        <input type="hidden" name="ticket_id" value="12345" />
        <input type="hidden" name="checksum" value="<?php echo md5('12345' . 'myprivatekey'); ?>" />
        <input type="submit" value="submit" />
    </form>
    

    At the backend, I verify if the ticket is correct by these lines:

    if(md5($_POST['ticket_id'] . 'myprivatekey') == $_POST['checksum']){
        // Go to go, input is valid
    }
    
  • joepie91joepie91 Member, Patron Provider
    edited April 2013

    @seikan said: Let me share my experience here. This is what I have done.

    At the backend, I verify if the ticket is correct by these lines:

    That does not protect against CSRF attacks.

  • what about checking session?

  • bnmklbnmkl Member
    edited April 2013

    As @joepie91 & @jcaleb mentioned. That does not work @seikan. You have to use sessions. Plus, the token can be random. It is only to validate that your app produced the submitted form.

    <?php
    
    if ($_POST['token'] === $_SESSION['token']) {
        // do something
    }
    
    $token = md5( uniqid( rand() ) );
    $_SESSION['token'] = $token;
    
    ?>
    
  • NexusNexus Member
    edited April 2013

    @raindog308 said: I can check that the user owns that ticket of course and do some other validation, but fundamentally, how do you present data to a user and safely get it back again in an HTML form?

    Use a database and check everything server side? :P (And spit it all out from the db?)

    intval is what you might be interested in for integer casting. (make sure you check for negatives or whatnot before insertion/etc)

    Hope this helps

  • raindog308raindog308 Administrator, Veteran

    @tortau said: My other recommendation is to use a web framework stack. Really. Trust me, it saves you a lot of headache. The established ones usually will handle session/cookies, CSRF token generated, post validation and properly escapes the form data, that all you really need is to check that your user has access/is owner to the ticket, and voila, save the new data.

    Yeah, I hear you. This is actually something that hooks into MyBB for user authentication/management, but not a forum interaction, so their post_key system isn't available.

    I can store a unique form token (unique to each form presentation) in the DB along with what variables are supposed to be present and their values, then check the id and the vars when I get it back. Is that sufficient to protect against CSRF?

    I'd like to use CodeIgniter, CakePHP, etc. but it's nice to have people login once without having to have a separate system, hence the custom code.

  • joepie91joepie91 Member, Patron Provider

    @raindog308 said: I can store a unique form token (unique to each form presentation) in the DB along with what variables are supposed to be present and their values, then check the id and the vars when I get it back. Is that sufficient to protect against CSRF?

    If you are going to store the values in the database anyway, then why are you taking input from the form data?

  • raindog308raindog308 Administrator, Veteran

    @joepie91 said: If you are going to store the values in the database anyway, then why are you taking input from the form data?

    Oh. Yeah. :-)

    I guess all the form needs is a token.

  • @joepie91 said: why are you

    ..randomly generated new ticket ID? Erm..ok. In a system that was coded by a fourth grader and doesn't work off a logged in..eRM, WELL, alright because it sounded like a fun question?

    @raindog308 said: I guess all the form needs is a token

    Yes. Tokens are good. Validation is good. Sanitization is good. Frameworks are good, libraries for form handlers et all usually take care of all of this..

    @joepie91 said: CSRF attacks

    Old. And too hypothetical. I'd rather worry about whether or not the Earth's core will stop spinning.

    @joepie91 said: and the user loads two different tickets

    You're saying don't use sessions because they could be poorly managed.

    Good session management is the key here. Thousands of scripts on every major site on the Internet rely on solid session and cookie management to solve all of these problems. I don't even think this was a real question.
    What were you guys really trying to test or inquire about by asking this...Comon now you can tell me..

    @Nexus said: Use a database

    Only well reasoned thought here. Howabout, wait for it...proper table relationships! You guys ever hear of a pumpkin table? This is programming 101. Yeah Yeah CSRF, Session stealing, LDAP injection, Path traversal and Buffer overflow Oh My!

    Is this what some one called my signature charm. /Sigh..Makes me want to jump out a f***in window.

  • bnmklbnmkl Member
    edited April 2013

    ... pumpkin table.

    I am logged into Google @natestamm and only getting "Goatse Tool" results.

    Could you elaborate on this, if possible, please ?

    ...with cherries on top.

    image

  • DomDom Member

    @natestamm What's a pumpkin table when it's at home?

  • WintereiseWintereise Member
    edited April 2013

    @bnmkl said: $token = md5( uniqid( rand() ) );

    Use mt-rand instead: http://jp1.php.net/manual/en/function.mt-rand.php

  • bnmklbnmkl Member

    Thanks @Wintereise.

    image

  • gbshousegbshouse Member, Host Rep

    Just add server side check - is currently logged client related to specific ticket number. We are trying to avoid client side check when possible, all validation stuff is done on server side.

  • joepie91joepie91 Member, Patron Provider

    @natestamm said: @joepie91 said: CSRF attacks

    Old. And too hypothetical. I'd rather worry about whether or not the Earth's core will stop spinning.

    @joepie91 said: and the user loads two different tickets

    You're saying don't use sessions because they could be poorly managed.

    Good session management is the key here. Thousands of scripts on every major site on the Internet rely on solid session and cookie management to solve all of these problems. I don't even think this was a real question.

    What were you guys really trying to test or inquire about by asking this...Comon now you can tell me..

    The fuck are you going on about?

  • perennateperennate Member, Host Rep

    Here's a very simple CSRF prevention script that doesn't require any changes to your code, just include the file and it should work: https://www.owasp.org/index.php/PHP_CSRF_Guard

    It does so by caching the output from the script, and then when PHP exits it changes any forms using POST to add a CSRF token which is stored in the session.

Sign In or Register to comment.