Signed tokens vs. session-based tokens - Page 3
Page 3 of 3 FirstFirst 123
Results 31 to 40 of 40

Thread: Signed tokens vs. session-based tokens

  1. #31
    Senior Member Derokorian's Avatar
    Join Date
    Apr 2011
    Location
    Denver
    Posts
    2,258
    Quote Originally Posted by sneakyimp View Post
    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.

    Quote Originally Posted by sneakyimp View Post
    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.
    Sadly, nobody codes for anyone on this forum. People taste your dishes and tell you what is missing, but they don't cook for you. ~anoopmail
    I'd rather be a comma, then a full stop.
    User Authentication in PHP with MySQLi - Don't forget to mark threads resolved - MySQL(i) warning

  2. #32
    Senior Member
    Join Date
    Apr 2003
    Location
    Flanders Fields
    Posts
    5,824
    Quote Originally Posted by Derokorian View Post
    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.
    IMPORTANT: STOP using the mysql extension. Use mysqli or pdo instead.
    World War One happened 100 years ago. Visit Old Grey Horror for the agony and irony.

  3. #33
    Senior Member
    Join Date
    Mar 2009
    Posts
    1,153
    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.

    PHP Code:
    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 $serverSecretstring $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 $serverSecretstring $userSecretstring $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));
        }

    Declare variables, not war.

  4. #34
    Senior Member
    Join Date
    Apr 2003
    Location
    Flanders Fields
    Posts
    5,824
    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.
    IMPORTANT: STOP using the mysql extension. Use mysqli or pdo instead.
    World War One happened 100 years ago. Visit Old Grey Horror for the agony and irony.

  5. #35
    Senior Member
    Join Date
    Mar 2009
    Posts
    1,153
    Quote Originally Posted by sneakyimp View Post
    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.

    Quote Originally Posted by sneakyimp View Post
    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.
    Declare variables, not war.

  6. #36
    Senior Member
    Join Date
    Apr 2003
    Location
    Flanders Fields
    Posts
    5,824
    Quote Originally Posted by Bonesnap View Post
    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
    PHP Code:
        /**
         * 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 $serverSecretstring $userSecret) : string
        
    {
            
    $token  bin2hex(random_bytes(32));
            
    $hash   hash_hmac(self::ALGORITHM$token$serverSecret.$userSecret);
            
    $signed $token.'-'.$hash;

            return 
    $signed;
        } 
    Quote Originally Posted by Bonesnap View Post
    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:
    PHP Code:
    /*
    |--------------------------------------------------------------------------
    | 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:
    PHP Code:
    $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.

    Quote Originally Posted by Bonesnap View Post
    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.
    Quote Originally Posted by Bonesnap View Post
    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?
    IMPORTANT: STOP using the mysql extension. Use mysqli or pdo instead.
    World War One happened 100 years ago. Visit Old Grey Horror for the agony and irony.

  7. #37
    Senior Member
    Join Date
    Mar 2009
    Posts
    1,153
    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.

    Quote Originally Posted by sneakyimp View Post
    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.
    Declare variables, not war.

  8. #38
    Senior Member Derokorian's Avatar
    Join Date
    Apr 2011
    Location
    Denver
    Posts
    2,258
    Quote Originally Posted by Bonesnap View Post
    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.


    Quote Originally Posted by Bonesnap View 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.
    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).
    Sadly, nobody codes for anyone on this forum. People taste your dishes and tell you what is missing, but they don't cook for you. ~anoopmail
    I'd rather be a comma, then a full stop.
    User Authentication in PHP with MySQLi - Don't forget to mark threads resolved - MySQL(i) warning

  9. #39
    Senior Member
    Join Date
    Mar 2009
    Posts
    1,153
    Quote Originally Posted by Derokorian View Post
    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.

    Quote Originally Posted by Derokorian View Post
    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.
    Declare variables, not war.

  10. #40
    Senior Member Derokorian's Avatar
    Join Date
    Apr 2011
    Location
    Denver
    Posts
    2,258
    Quote Originally Posted by Bonesnap View Post
    Well to be fair it didn't start out that way, but there's apparently no secure (or reliable) way to do it otherwise.
    You should look into the OAuth2 specification.
    Sadly, nobody codes for anyone on this forum. People taste your dishes and tell you what is missing, but they don't cook for you. ~anoopmail
    I'd rather be a comma, then a full stop.
    User Authentication in PHP with MySQLi - Don't forget to mark threads resolved - MySQL(i) warning

Thread Information

Users Browsing this Thread

There are currently 2 users browsing this thread. (0 members and 2 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •