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.

    Derokorian;11060695 wrote:

    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?

    As weedpacket just pointed out:

    weedpacket wrote:

    Note that under Kerckhoff's principle "compromised" here means "being able to read it", not necessarily alter it

    If an attacker can read ALL of your PHP source, but your credentials are stored somewhere else -- e.g., in an INI file or maybe injected into ENV by an apache configuration file or a .bashrc file or something -- then a well-designed system will resist giving up sensitive information.

    Derokorian;11060695 wrote:

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

    I would say that tokens being unique to a user is more important than expiry. Most exploits by hackers are only cost-effective if you can use them on a lot of people. An exploit of a single person is only worth the time & effort if that one person is really important somehow.

    Derokorian;11060695 wrote:

    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).

    Yes it could happen of course, but the odds are super super long. Even if you had 1017 users, how many will use exactly the same password? Also, a string with 17 upper and lowercase letters plus numbers presents a LOT of options for a salt. (I'm not sure exactly what salt possibilities are).

    Derokorian;11060695 wrote:

    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.

    I haven't had time yet to look at this in any detail. Looks interesting. I'd also point out that CodeIgniter and Laravel have built-in mechanisms to protect against CSRF. OWASP also offers some advice. Even Wikipedia has a brief section on prevention.

      sneakyimp wrote:

      Even if you had 1017 users, how many will use exactly the same password?

      A lot. I once worked with a users table that had hashed passwords, but they weren't salted; did you know the MD5 for "password" is "5f4dcc3b5aa765d61d8327deb882cf99"? I saw a lot of that. I also saw a lot of "827ccb0eea8a706c4c34a16891f84e7b" which led me to add an Easter Egg to the registration form, so that in certain cases it would respond with "That's amazing, I've got the same combination on my luggage."

      sneakyimp wrote:

      (I'm not sure exactly what salt possibilities are).

      For password_hash at least it's 128 bits chosen uniformly.

        Weedpacket;11060703 wrote:

        A lot. I once worked with a users table that had hashed passwords, but they weren't salted; did you know the MD5 for "password" is "5f4dcc3b5aa765d61d8327deb882cf99"? I saw a lot of that. I also saw a lot of "827ccb0eea8a706c4c34a16891f84e7b" which led me to add an Easter Egg to the registration form, so that in certain cases it would respond with "That's amazing, I've got the same combination on my luggage."

        For password_hash at least it's 128 bits chosen uniformly.

        If I'm doing this right, wolfram alpha tells me that 1017 users who all choose the exact same password still have a very small chance of randomly getting an identical salt of 128 bits. 1017 divided by 2128 is only 2.94x10-22. My stats muscles are quite rusty, though. I know the actual calculation is much more complicated...n choose k or something like that.

          Ah, well, you asked about "the same password", there; not password/salt combination.

            Weedpacket;11060709 wrote:

            Ah, well, you asked about "the same password", there; not password/salt combination.

            Yes, you are correct. I mean to imply that the likelihood of a salt collision was remote -- but even more remote if you took into account the possibility of distinct users choosing the exact same password. That implication is perhaps not quite correct as you've pointed out. Although I think most modern web apps enforce some modicum of password complexity.

              Derokorian;11060695 wrote:

              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;11060697 wrote:

              If an attacker can read ALL of your PHP source, but your credentials are stored somewhere else -- e.g., in an INI file or maybe injected into ENV by an apache configuration file or a .bashrc file or something -- then a well-designed system will resist giving up sensitive information.

              I suppose it depends on "nature of the access". If they just compromise $DOCROOT and FTP the PHP files out, or expose them in the browser and don't have access outside the webroot, then credentials stored in a file outside of $DOCROOT might allow you another layer of protection. But the end goal of most attacks is shell access or its equivalent and rooting of the entire box, and "game over" is a fairly decent description of a box that's had that done to it. I think that's Bonesnap & Derokorian's point there. If an attacker is smart enough to get a shell, or, worse yet, a root shell, you can bet they're smart enough to look for credentials in locations besides the DB class and/or the app's "config.php" file.

              Weedpacket;11060703 wrote:

              I also saw a lot of "827ccb0eea8a706c4c34a16891f84e7b" which led me to add an Easter Egg to the registration form, so that in certain cases it would respond with "That's amazing, I've got the same combination on my luggage."

              How many digits do they have on luggage down there? I'd expect something more like "e10adc3949ba59abbe56e057f20f883e"?

              Oh, and here are the passwords for a half billion accounts right here. 😉

                dalecosp;11060723 wrote:

                I suppose it depends on "nature of the access". If they just compromise $DOCROOT and FTP the PHP files out, or expose them in the browser and don't have access outside the webroot, then credentials stored in a file outside of $DOCROOT might allow you another layer of protection. But the end goal of most attacks is shell access or its equivalent and rooting of the entire box, and "game over" is a fairly decent description of a box that's had that done to it. I think that's Bonesnap & Derokorian's point there. If an attacker is smart enough to get a shell, or, worse yet, a root shell, you can bet they're smart enough to look for credentials in locations besides the DB class and/or the app's "config.php" file.

                Well if they can get to FTP, they can get to ish outside of the web accessible root. None of my config, or even classes, are in web accessible root. I have a public folder, this is the only thing accessible from the web. It contains only: .htaccess, index.php, scripts/ and styles/ literally everything else is not accessible from the web. Only root (or proper sudo privs) and apache user can access the config folder with sensitive credentials. I'm curious what makes that more "unsafe" than env vars? I really need to understand this, so I can determine if changing around my entire configuration scheme is worthwhile. If you mean accessing source on git, I have no problem with that, it doesn't give you anything besides how the app works - keys are still required and only stored on the server itself.

                However, my point was, if you can get to the source on the server, what is stopping you from just reading the files the same way the source does. I mean, either you have access to the box and can just open the files, or you have access to the source on disk and can just change it to dump the data to some file. I feel like I'm missing something.

                  Derokorian;11060739 wrote:

                  Well if they can get to FTP, they can get to ish outside of the web accessible root. None of my config, or even classes, are in web accessible root. I have a public folder, this is the only thing accessible from the web. It contains only: .htaccess, index.php, scripts/ and styles/ literally everything else is not accessible from the web. Only root (or proper sudo privs) and apache user can access the config folder with sensitive credentials.

                  This all sounds like very good practice. Most modern frameworks do try and keep the credentials out of the web root.

                  Derokorian;11060739 wrote:

                  I'm curious what makes that more "unsafe" than env vars? I really need to understand this, so I can determine if changing around my entire configuration scheme is worthwhile. If you mean accessing source on git, I have no problem with that, it doesn't give you anything besides how the app works - keys are still required and only stored on the server itself.

                  Your setup is sound and I wouldn't bother changing it. In fact, I was complaining about using environment variables just recently. The reason I mentioned kerckhoff's principle and environment variables was really just as a thought exercise to discuss the merits of Bonesnap's token class. If his class relied on randomly generated tokens, it might still work effectively even if someone reads the source code. Since the point of the token class is security, it seemed reasonable if we're talking about security to touch upon how the class might be compromised.

                  I hope by FTP access you mean SFTP (or more precisely SSH File Transfer Protocol) as old-fashioned FTP is not encrypted in transit. If someone is able to upload their own PHP source code to your server (or execute arbitrary PHP code as apache somehow), then injecting sensitive credentials as ENV variable provides no security advantage over storing them in a PHP file. If, on the other hand, someone were somehow able to read all of your PHP but not change anything, they probably would not be able to discover the contents of environment variables on your server.

                  Derokorian;11060739 wrote:

                  However, my point was, if you can get to the source on the server, what is stopping you from just reading the files the same way the source does. I mean, either you have access to the box and can just open the files, or you have access to the source on disk and can just change it to dump the data to some file. I feel like I'm missing something.

                  One example that comes to mind is when an apache adjustment or upgrade breaks your PHP installation and suddenly all of your PHP source code is visible to the entire world. I.e., every PHP file is not parsed as code but rather any visitor to the site will just see the PHP source code. It's been a long time, but this has in fact happened to me. Theoretical security is one thing, but in reality things break in funny ways and sometimes your access is not complete. SQL injection vulnerabilities, for example, don't compromise any of your own system, but attackers can sometimes siphon off your entire user table. The term defense in depth comes to mind. It's a bit like designing a warplane. It's all well and good to make a plane with a gun and bombs, but the A-10 warthog has all kinds of redundancy and armor and is intentionally designed so that it degrades gracefully when damaged. The Death Star, on the other hand, suffers a catastrophic failure once you get a torpedo or two into the exhaust port -- which is as big as a womp rat.

                    Derokorian;11060739 wrote:

                    Well if they can get to FTP, they can get to ish outside of the web accessible root.

                    That would, of course, depend on the configuration of the FTP root and the HTTPD Docroot. In some server configurations, or even per-user configuration (so depending on whose credentials are stolen(?)) the FTP access is chrooted to the server's DOCROOT, in others HOMEDIR and the HTTPD Docroot is a sub-dir of that, and I hope for goodness' sake no one has any FTP set up higher in the tree than that, but systems do have to be administered (I, for one know too many people who think that their convenience is more important that my security paranoia, and my paranoia is probably not even good enough for some of the companies out there). I must add that I had to smile a little when I read Docker's website, that they're basically just using the Linux equivalent of the FreeBSD jail that's been around since Who Knows When. I also have most of my super-secret stuff quite far away from the webroot, but in some situations I'm in some sort of "jailed" environment and not allowed that sort of access to other filesystem locations.

                    (Please also, reader of this thread, note that when I say FTP I mean SFTP; if you are still using plain-text FTP in this day & age you deserve to and should have your server rooted to teach you this lesson the hard way.)

                      dalecosp;11060755 wrote:

                      I also have most of my super-secret stuff quite far away from the webroot, but in some situations I'm in some sort of "jailed" environment and not allowed that sort of access to other filesystem locations.

                      I am fascinated by the idea of jails and have the rare occasion to use them, but recall that setting them up is a PITA.

                      dalecosp;11060755 wrote:

                      (Please also, reader of this thread, note that when I say FTP I mean SFTP; if you are still using plain-text FTP in this day & age you deserve to and should have your server rooted to teach you this lesson the hard way.)

                      This kind of thing does indeed cause a lot of soul-searching. Having some SQL injector try and steal all your users' (poorly) hashed passwords feels like a personal violation, as does having your email script abused to send spam. Such a low feeling 🙁

                        sneakyimp;11060747 wrote:

                        I hope by FTP access you mean SFTP (or more precisely SSH File Transfer Protocol) as old-fashioned FTP is not encrypted in transit. If someone is able to upload their own PHP source code to your server (or execute arbitrary PHP code as apache somehow), then injecting sensitive credentials as ENV variable provides no security advantage over storing them in a PHP file. If, on the other hand, someone were somehow able to read all of your PHP but not change anything, they probably would not be able to discover the contents of environment variables on your server.

                        If someone has FTP access to my server, they have root access. How do I know? First and foremost, only ssh port and web port are open. Second, I don't even have an ftp daemon installed. And thirdly, the ssh port is only open when my automated system needs to connect (basically, it hits the firewall service to open the port, connects to run commands, then closes the port back down). If someone gets into my system, they did some magical stuff, and I don't think it matters at that point how I store credentials haha.

                        sneakyimp;11060747 wrote:

                        One example that comes to mind is when an apache adjustment or upgrade breaks your PHP installation and suddenly all of your PHP source code is visible to the entire world. I.e., every PHP file is not parsed as code but rather any visitor to the site will just see the PHP source code. It's been a long time, but this has in fact happened to me. Theoretical security is one thing, but in reality things break in funny ways and sometimes your access is not complete. SQL injection vulnerabilities, for example, don't compromise any of your own system, but attackers can sometimes siphon off your entire user table. The term defense in depth comes to mind. It's a bit like designing a warplane. It's all well and good to make a plane with a gun and bombs, but the A-10 warthog has all kinds of redundancy and armor and is intentionally designed so that it degrades gracefully when damaged. The Death Star, on the other hand, suffers a catastrophic failure once you get a torpedo or two into the exhaust port -- which is as big as a womp rat.

                        All modifications are done through automation, and first tested on a staging server. I happen to be one of those people who is highly paranoid. It takes me 5-10min to open a shell to my production servers, but the benefit is - ssh isn't even available when I'm not on it.

                          6 days later
                          Derokorian;11060763 wrote:

                          If someone has FTP access to my server, they have root access. How do I know? First and foremost, only ssh port and web port are open. Second, I don't even have an ftp daemon installed. And thirdly, the ssh port is only open when my automated system needs to connect (basically, it hits the firewall service to open the port, connects to run commands, then closes the port back down). If someone gets into my system, they did some magical stuff, and I don't think it matters at that point how I store credentials haha.

                          I like your lockdown state.

                            Sneaky, you bring up great points. Thank you (and everyone else!) for the great discussion. I understand CSRF but obviously goofed in this instance; it's why code is posted and reviewed by others to find mistakes and possible bugs 🙂

                            Also apologies if I came off as being overly defensive - that was not my intention. I have made some modifications which I'll post below. The highlights are that the server secret is now a passed variable, so the implementation can be done however the developer wishes. There is also now a user secret, which is generated by random_bytes(32). These are both needed to generate and validate a token. The developer can choose how/when a user secret is generated. For me, I have done it on page load and store it in the session. If it exists then it doesn't generate a new one otherwise generate and store it, which can then be passed when verifying.

                            class Token
                            {
                            	/**
                            	 * The hashing algorithm used to hash the token data.
                            	 *
                            	 * @access public
                            	 * @var string
                            	 */
                            	const ALGORITHM = 'sha256';
                            
                            /**
                             * Generates a cryptographically signed token.
                             *
                             * @param string $serverSecret The server secret.
                             * @param string $userSecret   The user's secret.
                             *
                             * @return string Returns the new token.
                             */
                            public static function generate(string $serverSecret, string $userSecret) : string
                            {
                            	$token  = bin2hex(random_bytes(32));
                            	$hash   = hash_hmac(self::ALGORITHM, $token, $serverSecret.$userSecret);
                            	$signed = $token.'-'.$hash;
                            
                            	return $signed;
                            }
                            
                            /**
                             * Verifies a signed token.
                             *
                             * @param string $serverSecret The server secret.
                             * @param string $userSecret   The user secret.
                             * @param string $signedToken  The signed token to verify.
                             *
                             * @throws InvalidTokenException
                             * @throws TokenMismatchException
                             *
                             * @return void
                             */
                            public static function verify(string $serverSecret, string $userSecret, 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, $serverSecret.$userSecret)) {
                            		throw new TokenMismatchException('Token could not be verified!');
                            	}
                            }
                            
                            /**
                             * Generates a user secret for token validating.
                             *
                             * @return string Returns the user secret.
                             */
                            public static function generateUserSecret() : string
                            {
                            	return bin2hex(random_bytes(32));
                            }
                            }
                            

                              I think the ability to inject the values used in hashing is a good move because it would allow a dev to inject the values being hashed. I think the addition of $userSecret seems like a good idea -- and the static function to create a user secret is a nice touch. However, your class doesn't necessarily steer a dev toward best practices or offer features that protect this class against misuse.

                              It would be helpful to see all the situations where you use the class and hear more about how you plan to manage the $userSecret value. This might shake loose some more insights.

                                sneakyimp;11061045 wrote:

                                However, your class doesn't necessarily steer a dev toward best practices or offer features that protect this class against misuse.

                                Can you elaborate on this? What can be improved to "steer a dev toward best practices"?

                                In regards to protecting against misuse... I have mixed feelings about hand holding when it comes to this kind of stuff. At its core this class was created so I can use it when I start new projects. Instead of constantly copying a folder from one project to another I could simply just run a command and bring it in, or if I make changes to it I can run another command and it will update my project for me. I was going to keep it private on Bitbucket because Packagist supports private repositories (you just need an SSH key). But I thought or I could make it public and if someone finds a use for it, then all the better. If you're saying that this is harmful in general to the PHP landscape because of the potential for misuse, then I can just keep it private. But there is a laundry list of methods, classes, etc. in the PHP library that can be misused.

                                sneakyimp;11061045 wrote:

                                It would be helpful to see all the situations where you use the class and hear more about how you plan to manage the $userSecret value. This might shake loose some more insights.

                                All the situations was explained earlier in the thread. It's being used to generate a token that's passed with AJAX requests. If the token validates then the AJAX request proceeds otherwise there is an exception and an error message is returned to the user.

                                As described earlier the user secret is generated and stored in the session. If it already exists then it doesn't generate a new one. It's then used whenever a token is generated or validated.

                                  Bonesnap;11061131 wrote:

                                  Can you elaborate on this? What can be improved to "steer a dev toward best practices"?

                                  I think your class could benefit from more detail in the comments. Your most recent version of the class makes no mention at all of CSRF prevention -- this is its primary purpose, right? Or might it be used for something else? Or consider this function. Some detail in the comments might guide someone using it to better behaviors by providing guidelines for injected values

                                      /**
                                       * Generates a cryptographically signed token for use in forms to prevent CSRF exploits; assign to a hidden input in your form
                                       *
                                       * @param string $serverSecret The server secret: a cryptographically secure series of random bytes in string format; avoid storing this site-wide credential in web root or in your git repo
                                       * @param string $userSecret   The user's secret: a cryptographically secure series of random bytes unique to each session; used to generate and validate CSRF tokens; never display this value in HTML; should be stored in SESSION; regenerating or refreshing may cause AJAX functions to stop working in SPA
                                       *
                                       * @return string Returns the new token in string format
                                       */
                                      public static function generate(string $serverSecret, string $userSecret) : string
                                      {
                                          $token  = bin2hex(random_bytes(32));
                                          $hash   = hash_hmac(self::ALGORITHM, $token, $serverSecret.$userSecret);
                                          $signed = $token.'-'.$hash;
                                  
                                      return $signed;
                                  } 
                                  
                                  Bonesnap;11061131 wrote:

                                  In regards to protecting against misuse... I have mixed feelings about hand holding when it comes to this kind of stuff.

                                  I have mixed feelings too. CodeIgniter has a config value that you switch on or off and a handful of other config values:

                                  /*
                                  |--------------------------------------------------------------------------
                                  | Cross Site Request Forgery
                                  |--------------------------------------------------------------------------
                                  | Enables a CSRF cookie token to be set. When set to TRUE, token will be
                                  | checked on a submitted form. If you are accepting user data, it is strongly
                                  | recommended CSRF protection be enabled.
                                  |
                                  | 'csrf_token_name' = The token name
                                  | 'csrf_cookie_name' = The cookie name
                                  | 'csrf_expire' = The number in seconds the token should expire.
                                  | 'csrf_regenerate' = Regenerate token on every submission
                                  | 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
                                  */
                                  $config['csrf_protection'] = TRUE;
                                  $config['csrf_token_name'] = 'csrf_test_name';
                                  $config['csrf_cookie_name'] = 'csrf_cookie_name';
                                  $config['csrf_expire'] = 7200;
                                  $config['csrf_regenerate'] = TRUE;
                                  $config['csrf_exclude_uris'] = array();
                                  

                                  You set these values and then you have to take care to display the csrf token in every form yourself. The base class for a controller exposes a security property which lets you retrieve CSRF-related values:

                                  $this->security->get_csrf_token_name(); // returns the input name that will be checked when you submit a form
                                  return $this->security->get_csrf_hash(); // returns the current expected CSRF token
                                  

                                  I think it's pretty nice that CodeIgniter handles many aspects of the operation (generating the tokens, setting session/cookie/whatever, etc). On the other hand, I'm not entirely sure about the precise details of its functioning. I generally assume the CodeIgniter devs (or the CI community) has examined the internal behavior to make sure it does its job well. I realize this may not the case. If I had any comlaints about how it works in practice, a few come readily to mind:
                                  code comments reveal almost nothing about how it works and little about how to use it.
                                  The CSRF check seems to happen early in the application's bootstrapping code and afaik there is no way to intercept the event when CSRF fails -- and the error message is not very informative.
                                  The docs don't offer much more detail about how things work.
                                  There's no advice or guidance about how to deal with AJAX situations or any way to force regeneration of the token.

                                  In a lot of ways, I prefer your approach because it's more transparent. It doesn't really offer any advice on how to use it or what parameters to provide or what care one might want to take with the secret values supplied.

                                  Bonesnap;11061131 wrote:

                                  At its core this class was created so I can use it when I start new projects. Instead of constantly copying a folder from one project to another I could simply just run a command and bring it in, or if I make changes to it I can run another command and it will update my project for me. I was going to keep it private on Bitbucket because Packagist supports private repositories (you just need an SSH key). But I thought or I could make it public and if someone finds a use for it, then all the better. If you're saying that this is harmful in general to the PHP landscape because of the potential for misuse, then I can just keep it private. But there is a laundry list of methods, classes, etc. in the PHP library that can be misused.

                                  Bonesnap;11061131 wrote:

                                  All the situations was explained earlier in the thread. It's being used to generate a token that's passed with AJAX requests. If the token validates then the AJAX request proceeds otherwise there is an exception and an error message is returned to the user.

                                  I went back over the previous posts and perhaps I missed the PHP code that shows this class in use. Perhaps you could refer me to the post with PHP code where you generate a user secret, store it in session, and then check on form submission?

                                    Information about what the class does and how to use it would be included in something like a readme.md file or maybe at the top of the class. I just don't think that kind of information belongs in a PHP docs comment when describing what the parameters are, return types, etc. In my mind it's akin to reading a spec sheet on a car - you want the specs, not "what the car can do". That belongs in the brochure.

                                    sneakyimp;11061149 wrote:

                                    I went back over the previous posts and perhaps I missed the PHP code that shows this class in use. Perhaps you could refer me to the post with PHP code where you generate a user secret, store it in session, and then check on form submission?

                                    Sorry, I didn't write actual code or markup. I just described the steps of how the token's were being used in this post.

                                    At the top of the file before any markup is output I check the session. If $SESSION['userSecret'] exists, then I don't generate one. Otherwise I store $SESSION['userSecret'] = Token::generateUserSecret(). Then later in the markup in a hidden field or something I output <?php echo Token::generateToken(SERVER_SECRET, $SESSION['userSecret']); ?> which creates the token and hash tied to the user secret. Once an AJAX request is made, I pass that value along with it. In the code for the receiving AJAX file I check it: Token::verify(SERVER_SECRET, $SESSION['userSecret'], $postedToken). If it doesn't match than it throws an exception. Both the server and the user secret are required to generate and verify the tokens. That's it.

                                      Bonesnap;11061215 wrote:

                                      Information about what the class does and how to use it would be included in something like a readme.md file or maybe at the top of the class. I just don't think that kind of information belongs in a PHP docs comment when describing what the parameters are, return types, etc. In my mind it's akin to reading a spec sheet on a car - you want the specs, not "what the car can do". That belongs in the brochure.

                                      They belong in the docblocks for absolute certain. Two reasons: code hints in IDEs will display this text, so they user can read how to use it properly; and auto-documentation generators like phpDocumentor. Also, the brochure is a sale pitch, how to use a product doesn't belong there - that belongs in the manual. The manual is what can be auto-generated from the doc comments.

                                      Bonesnap;11061215 wrote:

                                      At the top of the file before any markup is output I check the session. If $SESSION['userSecret'] exists, then I don't generate one. Otherwise I store $SESSION['userSecret'] = Token::generateUserSecret(). Then later in the markup in a hidden field or something I output <?php echo Token::generateToken(SERVER_SECRET, $SESSION['userSecret']); ?> which creates the token and hash tied to the user secret. Once an AJAX request is made, I pass that value along with it. In the code for the receiving AJAX file I check it: Token::verify(SERVER_SECRET, $SESSION['userSecret'], $postedToken). If it doesn't match than it throws an exception. Both the server and the user secret are required to generate and verify the tokens. That's it.

                                      I have to say, I'm totally confused. I thought the point was to not rely on session? Doesn't this defeat the purpose? Now you have to send two tokens to validate the user can do a thing (the session token, and this signed token).

                                        Derokorian;11061227 wrote:

                                        They belong in the docblocks for absolute certain. Two reasons: code hints in IDEs will display this text, so they user can read how to use it properly; and auto-documentation generators like phpDocumentor. Also, the brochure is a sale pitch, how to use a product doesn't belong there - that belongs in the manual. The manual is what can be auto-generated from the doc comments.

                                        Fair points. I'll update the doc comments.

                                        Derokorian;11061227 wrote:

                                        I have to say, I'm totally confused. I thought the point was to not rely on session? Doesn't this defeat the purpose? Now you have to send two tokens to validate the user can do a thing (the session token, and this signed token).

                                        Well to be fair it didn't start out that way, but there's apparently no secure (or reliable) way to do it otherwise.

                                          Write a Reply...