I haven't been here in a while; I miss this place. Hope everyone is doing well. Without further adieu...

Up until recently I have always used the session-based method to implement security tokens. Page loads, a token is generated and printed to something like a hidden input field. The token is also stored in the session. When the form is submitted, the token goes along with it and the passed token is compared to what's in the session. If it matches, great, if not, some kind of error.

However, this has issues if you want to have multiple tabs open with the application or do asynchronous calls. As soon as another tab is opened, the token stored in the session is going to be overwritten, invalidating the first tab. Likewise, with asynchronous calls, you basically have to wait in queue which doesn't make them asynchronous at all.

The application I have been working on for my client was really hoping to be able to have multiple tabs open for their clients. Plus it is just convenient. So I did some searching online on how people handle this. One of the things I came across was signed tokens. Basically you remove the session part altogether and simply just verify the signed token when the form is sent. The entire value is a combination of a hashed value (with a server-side secret) and the token.

Below is a class I created to handle this (some code has been lifted from Stackoverflow):

/**
 * The Token class.
 */
class Token
{
	/**
	 * The hashing algorithm used to hash the token data.
	 *
	 * @access public
	 * @var string
	 */
	const ALGORITHM = 'sha256';

/**
 * Generates a cryptographically signed token.
 *
 * @return string Returns the new token.
 */
public static function generate() : string
{
	$token  = bin2hex(random_bytes(32));
	$hash   = hash_hmac(self::ALGORITHM, $token, TOKEN_SECRET);
	$signed = $token.'-'.$hash;

	return $signed;
}

/**
 * Verifies a signed token.
 *
 * @param string $signedToken The signed token to verify.
 *
 * @throws InvalidTokenException
 * @throws TokenMismatchException
 *
 * @return void
 */
public static function verify(string $signedToken)
{
	$parts = explode('-', $signedToken);

	if (count($parts) !== 2) {
		throw new InvalidTokenException('Invalid token form!');
	}

	list($token, $hash) = $parts;

	if ($hash !== hash_hmac(self::ALGORITHM, $token, TOKEN_SECRET)) {
		throw new TokenMismatchException('Token could not be verified!');
	}
}
}

The TOKEN_SECRET can be whatever you want, obviously stored outside the web root in a secure place. You could add some additional security by adding a timestamp to the token to allow expirations, or the user's ID so the tokens generated during their use of the application would be exclusive to them.

I much prefer this method now as it's much simpler to implement and maintain than what I was doing previously. Also just feels much more elegant.

Any feedback would be greatly appreciated. Would also be interested in hearing your methods of implementing security tokens.

Thanks! 🙂

    Well, it looks very nice ... seriously. Perhaps it needs this though:

    function __construct() {
    
       if (PHP_MAJOR_VERSION < 7) {
          throw new VersionMismatchException('PHP Version Insufficient!');
       }
    
    }

    As for my way of doing it, I probably don't do it enough. Some of our systems go way back to 5.2 days (eek!) and so does the code, and tokens are generally used mostly for AJAX stuff and is based on hashing constants and secrets, server names and timestamps and poems and such-like ...

      Bonesnap;11060243 wrote:

      I haven't been here in a while; I miss this place. Hope everyone is doing well. Without further adieu...

      Up until recently I have always used the session-based method to implement security tokens. Page loads, a token is generated and printed to something like a hidden input field. The token is also stored in the session. When the form is submitted, the token goes along with it and the passed token is compared to what's in the session. If it matches, great, if not, some kind of error.

      However, this has issues if you want to have multiple tabs open with the application or do asynchronous calls. As soon as another tab is opened, the token stored in the session is going to be overwritten, invalidating the first tab. Likewise, with asynchronous calls, you basically have to wait in queue which doesn't make them asynchronous at all.

      I haven't gotten to the meat of your post yet, still working, but for this problem I use an array in session. Like this:

      class Token implement Serializable {
          use magicGet, serialize;
      
      protected $form;
      protected $token;
      protected $expiry;
      
      public function __construct(string $form) {
          $this->form = $form;
          $this->token = bin2hex(random_bytes(16));
          $this->expiry = new DateTime()->sub(new DateInterval('PT1H'));
      }
      
      public function getToken() {
          return $this->token;
      }
      
      public function validate($form) {
          return $this->form == $form && (new DateTime()) <= $this->expiry;
      }
      }
      
      // Adding a token
      $token = new Token('update_user');
      $_SESSION['tokens'][$token->getToken()] = $token;
      
      // Checking a token
      if (!isset($_SESSION['tokens'][$_POST['token']]) || $_SESSION['tokens'][$_POST['token']]->validate('update_user')) {
          handleError('invalid token supplied');
      }
      

      Actually adding a token and checking a token are handled through a collection class that's pretty indepth but nicely encapsulates all the logic. This way I can add multiple tokens in a single page load as well (IE: login form, register form and forgot password form, which all load in a single request, though register and forgot are hidden until needed).

      I will look at the rest of your post this evening and give some feedback, but I'm sure its bomb 😛

        dalecosp;11060253 wrote:

        Well, it looks very nice ... seriously. Perhaps it needs this though:

        function __construct() {
        
           if (PHP_MAJOR_VERSION < 7) {
              throw new VersionMismatchException('PHP Version Insufficient!');
           }
        
        }

        As for my way of doing it, I probably don't do it enough. Some of our systems go way back to 5.2 days (eek!) and so does the code, and tokens are generally used mostly for AJAX stuff and is based on hashing constants and secrets, server names and timestamps and poems and such-like ...

        Dale that is unnecessary, as this file will not compile in pre-php7 to be able to run the version compare anyway.

          Derokorian;11060257 wrote:

          Dale that is unnecessary, as this file will not compile in pre-php7 to be able to run the version compare anyway.

          Well, it was kind of a side comment that it wasn't mentioned. I'd wasted some time trying to run random_bytes() in the CLI and couldn't figure out why it didn't work until I went to the manual. Placing it in a script would've probably given me an error log entry, or at least I could've set error_reporting() up and gotten some feedback instead of scratching my head for a few minutes. 😉

            dalecosp;11060261 wrote:

            Well, it was kind of a side comment that it wasn't mentioned. I'd wasted some time trying to run random_bytes() in the CLI and couldn't figure out why it didn't work until I went to the manual. Placing it in a script would've probably given me an error log entry, or at least I could've set error_reporting() up and gotten some feedback instead of scratching my head for a few minutes. 😉

            Ah cuz you just tried to use some function in it, if you tried to run the whole class, the parser would have died on the return type and scalar type hint 🙂

              (I won't tell you about the kludge I wrote today to deal with a PHP 5.3 issue. :o )

                NogDog;11060265 wrote:

                (I won't tell you about the kludge I wrote today to deal with a PHP 5.3 issue. :o )

                I, for one, will sleep MUCH sounder when a particular domain of our current 5 dozen runs on something that was actually released during the current decade.... :eek:

                  Thanks for the replies everyone!

                  dalecosp;11060253 wrote:

                  Well, it looks very nice ... seriously. Perhaps it needs this though:

                  function __construct() {
                  
                     if (PHP_MAJOR_VERSION < 7) {
                        throw new VersionMismatchException('PHP Version Insufficient!');
                     }
                  
                  }

                  As for my way of doing it, I probably don't do it enough. Some of our systems go way back to 5.2 days (eek!) and so does the code, and tokens are generally used mostly for AJAX stuff and is based on hashing constants and secrets, server names and timestamps and poems and such-like ...

                  Eventually I'd like to add it to Packagist and in doing so put a PHP >= 7 requirement on it. I have finally found the convenience and benefits of using Composer and now I'm not sure I could go without it.

                  I couldn't imagine working in 5.2...

                  Derokorian;11060255 wrote:

                  ... but for this problem I use an array in session ...

                  Actually adding a token and checking a token are handled through a collection class that's pretty indepth but nicely encapsulates all the logic. This way I can add multiple tokens in a single page load as well (IE: login form, register form and forgot password form, which all load in a single request, though register and forgot are hidden until needed).

                  I saw solutions that involved an array, but it seemed complicated to me and this solution seemed like it would work better with the least amount of refactoring (we had them across many pages). The above code is admittedly quite simple as I haven't implemented any additional security like expiration date, etc. We had thought adding it for our application but the user base could suffer for it so we decided what we had was still secure (an attacker would have to know the secret anyway, which means we had already lost).

                  A general-use collection class (that's abstract) is something else I have been working on, too. I may post it here for feedback in the near future.

                     $signed = $token.'-'.$hash; 

                    This part seriously concerns me. You're given them the token and the resulting hash, given enough of these combinations a hacker can guess the secret, since its always the same I believe. I would use a separate key, and encrypt that entire string with openssl_encrypt, then when you check you can use this key to decrypt to the string above. I think this would be more secure.

                     $signed = $token.'-'.$hash; 
                     $signed = openssl_encrypt($signed, 'AES-256-CBC', ENCRYPT_PASS);
                    
                    public static function verify(string $signedToken) 
                    { 
                        $signedToken = openssl_decrypt($signedToken, 'AES-256-CBC', ENCRYPT_PASS);
                        $parts = explode('-', $signedToken); 
                    

                      The first thing that occurs to me is that TOKEN_SECRET should probably not be defined as a constant. To define it as a constant suggests that you set one value which does not change. It also smells like Service Locator/Factory patterns to me. I think some kind of dependency injection approach would be better. E.g., pass in $token_secret as a var to a constructor? Or if you really want static methods, assign some value to Token::$token_secret? It would make your class more flexible if a user could choose their own method of defining $token_secret rather than having to define a constant somewhere? E.g., reading it from a config file or a cookie or something.

                      Philosophically, and from a security standpoint, the idea that there is one TOKEN_SECRET defined somewhere in a constants file doesn't sound all that secure to me. I don't really know how you are defining TOKEN_SECRET but my guess is that it is consistent from page access to page access. My security muscles have atrophied a bit, but have you considered applying Kerckhoff's principle? I.e., if someone were to read your source, would they gain access to TOKEN_SECRET?

                      Also, if you allow any random visitor to see a possibly unlimited number of token-hash combinations, you are badly exposing your system to a known-plaintext attack.

                      Perhaps you could provide some very simple usage examples for discussion?

                        sneakyimp;11060399 wrote:

                        The first thing that occurs to me is that TOKEN_SECRET should probably not be defined as a constant. To define it as a constant suggests that you set one value which does not change. It also smells like Service Locator/Factory patterns to me. I think some kind of dependency injection approach would be better. E.g., pass in $token_secret as a var to a constructor? Or if you really want static methods, assign some value to Token::$token_secret? It would make your class more flexible if a user could choose their own method of defining $token_secret rather than having to define a constant somewhere? E.g., reading it from a config file or a cookie or something.

                        Philosophically, and from a security standpoint, the idea that there is one TOKEN_SECRET defined somewhere in a constants file doesn't sound all that secure to me. I don't really know how you are defining TOKEN_SECRET but my guess is that it is consistent from page access to page access. My security muscles have atrophied a bit, but have you considered applying Kerckhoff's principle? I.e., if someone were to read your source, would they gain access to TOKEN_SECRET?

                        Also, if you allow any random visitor to see a possibly unlimited number of token-hash combinations, you are badly exposing your system to a known-plaintext attack.

                        Perhaps you could provide some very simple usage examples for discussion?

                        The secret absolutely has to be constant from page-load to page load for a given environment. Think of it like your private key file, it never changes but is needed on each ssh to validate the connection is secure. If the token changed, you'd never be able to validate the token given back. As for the plain-text: see my comment about encrypting the whole string, so that you don't have access to the token and the signature.

                          Derokorian;11060401 wrote:

                          The secret absolutely has to be constant from page-load to page load for a given environment.

                          I realize the goal is to allow multi-tab and/or AJAX requests and or SPAs so there is some desire to avoid having the token change too often. This does not mean that we have to define the value as a constant in code using [man]define[/man]. I would say that any value that depends on the current user's id should not be defined in a constant. I'd also point out that password hashing best practices dictate the use of a salt that is different for each and every password. I wouldn't claim to be any sort of supreme expert in this kind of thing, but I would imagine that a single TOKEN_SECRET for one's site might not be ideal -- especially if you could improve matters by storing a distinct $token_secret for each session.

                          Derokorian;11060401 wrote:

                          Think of it like your private key file, it never changes but is needed on each ssh to validate the connection is secure. If the token changed, you'd never be able to validate the token given back.

                          If I'm not mistaken, the ssh protocol works by authenticating one's private key initially but then another per-session key is generated for the duration of the ssh session. Also, ssh keys are quite large -- typically 2048 or more bits these days.

                          Derokorian;11060401 wrote:

                          As for the plain-text: see my comment about encrypting the whole string, so that you don't have access to the token and the signature.

                          I think this is an improvement, but I would want to see details of how bonesnap intends to use his Token class. So far it only does two things: 1) generate a random token and hash it using TOKEN_SECRET defined as a constant somewhere and return token-hash and then 2) verify that a given token-hash is hashed the same way he hashes things. How are these tokens used? Does every AJAX request generated on a page use the exact same token-hash string? How is TOKEN_SECRET generated? If it's just a fixed value in a constants file and he's using that class above, then any valid token-hash will be valid forever (or until he changes his TOKEN_SECRET in his source code) and there's nothing to stop Mallory from getting one valid token-hash value and using it to forge requests for Alice, Bob, or whoever. We'd have to see more specifics to determine how good it is.

                            sneakyimp wrote:

                            If I'm not mistaken, the ssh protocol works by authenticating one's private key initially but then another per-session key is generated for the duration of the ssh session.

                            Public-key encryption/decryption like RSA is slow with a lot of extra back-and-forth. So instead of using it for actual communication, a symmetric cipher (like AES) is used instead. The hangup with using a symmetric cipher though is that both parties have to have the same key. How can one party generate such a key and communicate it to the other without it being snooped along the way ... oh. Right.

                              Weedpacket;11060411 wrote:

                              Public-key encryption/decryption like RSA is slow with a lot of extra back-and-forth. So instead of using it for actual communication, a symmetric cipher (like AES) is used instead. The hangup with using a symmetric cipher though is that both parties have to have the same key. How can one party generate such a key and communicate it to the other without it being snooped along the way ... oh. Right.

                              This limits the use of one's precious private key to the initial handshake, thereby limiting its exposure to attack (i.e., it won't be used over and over and over again in a long conversation). Furthermore, if one session key is somehow cracked, the attacker only has insight into that one session, and not other sessions.

                                Derokorian;11060335 wrote:

                                This part seriously concerns me. You're given them the token and the resulting hash, given enough of these combinations a hacker can guess the secret, since its always the same I believe. I would use a separate key, and encrypt that entire string with openssl_encrypt, then when you check you can use this key to decrypt to the string above. I think this would be more secure.

                                 $signed = $token.'-'.$hash; 
                                 $signed = openssl_encrypt($signed, 'AES-256-CBC', ENCRYPT_PASS);
                                
                                public static function verify(string $signedToken) 
                                { 
                                    $signedToken = openssl_decrypt($signedToken, 'AES-256-CBC', ENCRYPT_PASS);
                                    $parts = explode('-', $signedToken); 
                                

                                I tried to avoid using something that wasn't part of the SPL. My original implementation used openssl_random_pseudo_bytes(), but then I had to check to make sure the extension was enabled. That is why I switched to using random_bytes() because it's included by default in PHP 7, and there are no checks or exceptions being thrown in the generation of a token.

                                Is there some kind of fallback in the SPL that can be used if the OpenSSl extension is not loaded?

                                Also in my very cursory testing with openssl_encrypt, you must provide an initialization vector. When I tried to run your code I got a PHP warning (or a notice - I forget which). And from what I can tell you must provide the same initialization vector to decrypt it, which kinda defeats it since it would have to be generated every time and then passed every time, but to pass it every time means it has to be available client-side.

                                In any case, I'm not worried about someone "guessing" the secret. Granted, it can be as complex as the developer would like it to be, but in my case it's a string of 100 random characters, whose character set includes every character on the standard keyboard minus the space, single quote, double quote, and back slash. No supercomputer on the planet could crack that in a trillion years.

                                I like the idea, however, so if there is a proven fallback in the SPL I see no reason not to implement it.

                                  sneakyimp;11060399 wrote:

                                  The first thing that occurs to me is that TOKEN_SECRET should probably not be defined as a constant. To define it as a constant suggests that you set one value which does not change. It also smells like Service Locator/Factory patterns to me. I think some kind of dependency injection approach would be better. E.g., pass in $token_secret as a var to a constructor? Or if you really want static methods, assign some value to Token::$token_secret? It would make your class more flexible if a user could choose their own method of defining $token_secret rather than having to define a constant somewhere? E.g., reading it from a config file or a cookie or something.

                                  Philosophically, and from a security standpoint, the idea that there is one TOKEN_SECRET defined somewhere in a constants file doesn't sound all that secure to me. I don't really know how you are defining TOKEN_SECRET but my guess is that it is consistent from page access to page access. My security muscles have atrophied a bit, but have you considered applying Kerckhoff's principle? I.e., if someone were to read your source, would they gain access to TOKEN_SECRET?

                                  Also, if you allow any random visitor to see a possibly unlimited number of token-hash combinations, you are badly exposing your system to a known-plaintext attack.

                                  Perhaps you could provide some very simple usage examples for discussion?

                                  I'll start with a simple usage example:

                                  1. Page loads and a token is generated using Token::generate().
                                  2. It's printed in a hidden input field (or something similar).
                                  3. An AJAX call is made. The value in the hidden input field is passed as part of the request.
                                  4. In the PHP file on the server-side, the token is verified using Token::verify($thePostedToken).
                                  5. If they don't match, throw an exception or error out or do whatever you want.
                                  6. If they do match, then proceed.

                                  So the intent is to keep it constant - hence the use of the constant. The developer can define it anyway they want to.

                                  Kerckhoff's Principle honestly doesn't make much sense to me. It sounds great but to me isn't grounded in reality for a very simple reason: if an attacker has access to my source code then it's game over. There's no security in the world that can handle that. Furthermore if that were to ever happen, the last thing I care about is my token. I don't give a rodent's rear at that point - there are far larger fish to fry. You know what else I have stored away in my source code? Database credentials, PHPMailer credentials, etc. I have bigger problems than a token.

                                  sneakyimp wrote:

                                  I would say that any value that depends on the current user's id should not be defined in a constant.

                                  The token secret has nothing to do with the user's ID.

                                  sneakyimp wrote:

                                  I'd also point out that password hashing best practices dictate the use of a salt that is different for each and every password.

                                  This has nothing to do with passwords so I am not sure why it was mentioned. Also, each hash will be different anyway since the token will be different.

                                  sneakyimp wrote:

                                  How is TOKEN_SECRET generated?

                                  However the developer would like. For me, I used KeePass and generated a random 100 character string.

                                  sneakyimp wrote:

                                  If it's just a fixed value in a constants file and he's using that class above, then any valid token-hash will be valid forever (or until he changes his TOKEN_SECRET in his source code) and there's nothing to stop Mallory from getting one valid token-hash value and using it to forge requests for Alice, Bob, or whoever.

                                  In my specific example, yes, but as I mentioned in my original post you could harden it by including an expiration during hashing or a user's ID, etc. That is left up to the developer.

                                    5 days later
                                    Bonesnap;11060421 wrote:

                                    I'll start with a simple usage example:

                                    1. Page loads and a token is generated using Token::generate().
                                    2. It's printed in a hidden input field (or something similar).
                                    3. An AJAX call is made. The value in the hidden input field is passed as part of the request.
                                    4. In the PHP file on the server-side, the token is verified using Token::verify($thePostedToken).
                                    5. If they don't match, throw an exception or error out or do whatever you want.
                                    6. If they do match, then proceed.

                                    So the intent is to keep it constant - hence the use of the constant. The developer can define it anyway they want to.

                                    Assuming TOKEN_SECRET is constant and your token class is defined as above, then doesn't that mean that any output of Token::generate() will never expire and any token generated is valid for any visitor? If so, this doesn't protect against anything at all because an attacker can simply visit your site, view the form, take some valid token from the hidden input field, and then concoct a malicious form hosted on any domain under the sun, and if they trick one of your users into submitting it, then your site will accept that token and the attack will succeed. In my understanding, the way you defend against CSRF is to construct tokens with a short lifespan that are valid only for one particular user. If any valid token is also valid for any visitor, then you are not protected at all against CSRF. You may want to review owasp CSRF prevention cheatsheet.

                                    Bonesnap;11060421 wrote:

                                    Kerckhoff's Principle honestly doesn't make much sense to me. It sounds great but to me isn't grounded in reality for a very simple reason: if an attacker has access to my source code then it's game over.

                                    KP is a principle to guide security review. It's complicated but basically boils down to suggestions like: good encryption systems are still good even if the entire mechanism of encryption is known. Keep your keys secure and ideally separate from your source (e.g., in an environment variable somewhere). In practice, PHP applications are totally compromised if the source code gets compromised, but alls is not lost if attackers only manage some SQL injection. Ideally an SQL security failure wouldn't compromise the security of your data. In applying it to your token-generating scheme, the lesson I take away is that your system generates ALL of its tokens using one single constant. I suggest you improve your system by having it generate tokens that also depend on a user-specific (or session-specific) value.

                                    Bonesnap;11060421 wrote:

                                    There's no security in the world that can handle that. Furthermore if that were to ever happen, the last thing I care about is my token. I don't give a rodent's rear at that point

                                    If your PHP source were compromised, but all your sensitive credentials injected as envrionment variables (e.g., via an apache conf file) then your system would not necessarily be compromised.

                                    Bonesnap;11060421 wrote:

                                    Database credentials, PHPMailer credentials, etc. I have bigger problems than a token.

                                    Yes this is how most PHP apps work. I'm not trying to be pedantic. I do mean to suggest that I think your CSRF protection is perhaps not providing the protection you think it is.

                                    Bonesnap;11060421 wrote:

                                    The token secret has nothing to do with the user's ID.

                                    I think the token should have something that makes it specific to each visitor. If the visitor is not yet authenticated, you can still maintain a session using a session ID and store something unique for each user so that every user who visits will receive tokens that are only valid for them and no one else. This could be a randomly generated session ID stored in a cookie, for instance.

                                    Bonesnap;11060421 wrote:

                                    This has nothing to do with passwords so I am not sure why it was mentioned. Also, each hash will be different anyway since the token will be different.

                                    The reason I mention passwords is because MD5 hashing and SHA1 hashing are being deprecated, with more likely to follow -- there's a cautionary tale here. The best-practice of using a salt when hashing passwords and the fairly recent introduction of [man]password_hash[/man] is a to improve security. Not only is each password different, but we generate a random salt to hash that password to further insulate our hashed values against attacks. Not only does each password hash to a different value, but the same password, used twice, will never have the same hash thanks to the salt.

                                    Bonesnap;11060421 wrote:

                                    However the developer would like. For me, I used KeePass and generated a random 100 character string.

                                    Depending on the charset used, that sounds like 800 bits or so, which is pretty hefty. The risk, as I've pointed out lies primarily in the fact that a token that works for me also works for you. If I'm an attacker, I view your form, I extract a valid token, and I construct a malicious form you using the token and the token will still be accepted. I just have to trick you into clicking on my form. This is the essence of a CSRF attack. CSRF prevention hinges upon insuring that tokens valid for me are NOT valid for anyone else.

                                    Bonesnap;11060421 wrote:

                                    In my specific example, yes, but as I mentioned in my original post you could harden it by including an expiration during hashing or a user's ID, etc. That is left up to the developer.

                                    It may be impactical for any given project to invest in CSRF protection if the risk of attack is low, but you only have to do it right once. It is the expiration of a token that introduces the trouble when you have a lot of AJAX, a SPA, or multiple windows open. You can probably relax your expiration times and let your tokens last for hours without much risk. However, I think it's critically important that a token displayed in hidden input in User A's browser should not work at all for User B. You should store a secret random token in session or in a cookie -- a different one generated for each visitor -- so that User A cannot attack User B.

                                      sneakyimp wrote:

                                      In practice, PHP applications are totally compromised if the source code gets compromised, but alls is not lost if attackers only manage some SQL injection.

                                      Note that under Kerckhoff's principle "compromised" here means "being able to read it", not necessarily alter it (KP is why algorithms like Rijndael and Keccak (aka AES and SHA3) can be published in full and yet still be considered secure enough for government work). A potential attacker could, for example "compromise" the PHP application in this context by reading the first post in this thread; do that and work on crafting an attack that will break security, knowing in advance how the application will respond to messages that it is sent - they don't need access to the actual system to do so. KP means that knowing how the system works isn't enough to make that system vulnerable.

                                        sneakyimp;11060691 wrote:

                                        If your PHP source were compromised, but all your sensitive credentials injected as envrionment variables (e.g., via an apache conf file) then your system would not necessarily be compromised.

                                        I'll admit, this makes no sense to me. If they can get to the source, does it really matter how the credentials are stored since the source obviously has to use them? They obviously now have the knowledge to know where the credentials come from to look them up?

                                        sneakyimp;11060691 wrote:

                                        Yes this is how most PHP apps work. I'm not trying to be pedantic. I do mean to suggest that I think your CSRF protection is perhaps not providing the protection you think it is.

                                        I think the token should have something that makes it specific to each visitor. If the visitor is not yet authenticated, you can still maintain a session using a session ID and store something unique for each user so that every user who visits will receive tokens that are only valid for them and no one else. This could be a randomly generated session ID stored in a cookie, for instance.

                                        I agree, a token should only work for a specific visitor and should have an expiry time less than or equal to session expiry.

                                        sneakyimp;11060691 wrote:

                                        The reason I mention passwords is because MD5 hashing and SHA1 hashing are being deprecated, with more likely to follow -- there's a cautionary tale here. The best-practice of using a salt when hashing passwords and the fairly recent introduction of [man]password_hash[/man] is a to improve security. Not only is each password different, but we generate a random salt to hash that password to further insulate our hashed values against attacks. Not only does each password hash to a different value, but the same password, used twice, will never have the same hash thanks to the salt.

                                        Should say: will LIKELY never have the same hash, thanks to the generated salt. There is always a possibility of collision, that possibility increases with userbase, and decreases with salt length (which IIRC is 17 in password_hash).

                                        sneakyimp;11060691 wrote:

                                        It may be impactical for any given project to invest in CSRF protection if the risk of attack is low, but you only have to do it right once. It is the expiration of a token that introduces the trouble when you have a lot of AJAX, a SPA, or multiple windows open. You can probably relax your expiration times and let your tokens last for hours without much risk. However, I think it's critically important that a token displayed in hidden input in User A's browser should not work at all for User B. You should store a secret random token in session or in a cookie -- a different one generated for each visitor -- so that User A cannot attack User B.

                                        To that end, I think what you might be looking for is more along the lines of JWT if you're trying to not be session/cookie dependent.