PHP - CSRF - How to make it works in all tabs?

前端 未结 2 1313
盖世英雄少女心
盖世英雄少女心 2020-12-01 01:56

I have read about how to prevent CSRF-attacks in the last days. I am going to update the token in every pageload, save the token in the session and make a check when submitt

相关标签:
2条回答
  • 2020-12-01 02:21

    Yes, with the stored-token approach you'd have to keep all generated tokens just in case they came back in at any point. A single stored-token fails not just for multiple browser tabs/windows but also for back/forward navigation. You generally want to manage the potential storage explosion by expiring old tokens (by age and/or number of tokens issued since).

    Another approach that avoids token storage altogether is to issue a signed token generated using a server-side secret. Then when you get the token back you can check the signature and if it matches you know you signed it. For example:

    // Only the server knows this string. Make it up randomly and keep it in deployment-specific
    // settings, in an include file safely outside the webroot
    //
    $secret= 'qw9pDr$wEyq%^ynrUi2cNi3';
    
    ...
    
    // Issue a signed token
    //
    $token= dechex(mt_rand());
    $hash= hash_hmac('sha1', $token, $secret);
    $signed= $token.'-'.$hash;
    
    <input type="hidden" name="formkey" value="<?php echo htmlspecialchars($signed); ?>">
    
    ...
    
    // Check a token was signed by us, on the way back in
    //
    $isok= FALSE;
    $parts= explode('-', $_POST['formkey']);
    if (count($parts)===2) {
        list($token, $hash)= $parts;
        if ($hash===hash_hmac('sha1', $token, $secret))
            $isok= TRUE;
    }
    

    With this, if you get a token with a matching signature you know you generated it. That's not much help in itself, but then you can put extra things in the token other than the randomness, for example user id:

    $token= dechex($user->id).'.'.dechex(mt_rand())
    
    ...
    
        if ($hash===hash_hmac('sha1', $token, $secret)) {
            $userid= hexdec(explode('.', $token)[0]);
            if ($userid===$user->id)
                $isok= TRUE
    

    Now each form submission has to be authorised by the same user who picked up the form, which pretty much defeats CSRF.

    Another thing it's a good idea to put in a token is an expiry time, so that a momentary client compromise or MitM attack doesn't leak a token that'll work for that user forever, and a value that is changes on password resets, so that changing password invalidates existing tokens.

    0 讨论(0)
  • 2020-12-01 02:30

    You could simply use a token which is persistent for the current session or even the user (e.g. a hash of the hash of the user's password) and cannot be determined by a third party (using a hash of the user's IP is bad for example).

    Then you don't have to store possibly tons of generated tokens and unless the session expires (which would probably require the user to login again anyway) the user can use as many tabs as he wants.

    0 讨论(0)
提交回复
热议问题