In an API I have there’s a requirement to use an authentication method other than OAuth2 or any kind of token generation which requires making an extra HTTP call.

With this in mind there’s this https://www.xml.com/pub/a/2003/12/17/dive.html
I’ve only stored passwords as hashes and used functions like password_verify to know the user sent the proper credentials without actually knowing the password stored in DB.
WSSE requires to encrypt with SHA1 the credentials being sent, which means the API needs to retrieve the password in plain text to recreate the digest and compare it to the one sent by the user.
So, how should I be storing this password if the code needs it to recreate the hash?
Should I have something like a master password and store them encrypted instead of hashed?


Most of the information I’ve found about WSSE is very very old, and some implementations have it marked as deprecated, do you know any other type of standard authentication where the user can generate the token instead of having to make an extra HTTP call?

  • @pe1ucaOP
    link
    fedilink
    19 months ago

    Based on the title you’re right, I asked about how to do X when probably I need to do Y, but the first and last paragraphs mention what’s my requirement: a for of authentication which doesn’t require to make an extra HTTP call to generate a token.

    And what I mean by this is OAuth specifies the client needs to request an access token and an optional refresh token to the authorization server, afterwards the access token can be sent to the resource server (in this case my API), if the token expires the client can make another request to the authorization server with the refresh token.
    Each call to the authorization server is that “extra http call” I mentioned.

    Currently the only solution I found which seemed somewhat secure was WSSE, but again, I’ve only worked with OAuth2 and hashing passwords (or even better, using a dedicated service like keycloak), so I’m not sure what’s the best option to store the data it requires or if there’s a better solution.

    I don’t know how to be more clear, is there a way to authenticate a client to the resource server (my API) without making the client call endpoints to generate the tokens? Is there a way for the client to generate their own tokens and for me to validate them?

    • @towerful@programming.dev
      link
      fedilink
      3
      edit-2
      9 months ago

      As for client side token generation…
      Never trust the client.

      Say you hash the password client side. At this point, you have to have static salt (which can be extracted from clients), and the hashed result becomes the password.
      All of this greatly weakens the security.

      If the client sends a username, and the server returns a salt, then it’s a bit more secure. At least this way the salt can be randomly generated for each user.
      But, it’s an extra API call.

      You could use the username as the salt. This makes things a bit better, but you open yourself to being rainbow-tabled for usernames like “admin”. Also, the salt doesn’t change when a password is updated.

      Here’s a SE post that kinda pertains to what you want:
      https://security.stackexchange.com/questions/93395/how-to-do-client-side-hashing-of-password-using-bcrypt

      This one has a section on client side hashing:
      https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords/31846#31846


      Edit:

      Client side key generation isn’t worth it. There aren’t any good implementations.
      I think one of the answers linked above alludes to the following solution:

      Do a double salt:

      Username & password get bcrypted (or similar) together in the client. The username-as-salt reduces the parallel of brute force attacks to a single user (ie if an attacker has a bunch of hashes of different passwords for the same user, they can brute force them all at the same time - they know the salt)

      Username and hash is sent along with the request (the hash essentially becomes the new password without leaking it in plaintext in server logs, gateways, proxies etc.).

      The server then retrieves the users salt from the database, hashes it again using the appropriate salt.

      This way, at least the data at rest is fairly well protected. But I’m not a crypto guy. I have learned to follow the herd when it comes to authentication and security.
      And I don’t think there is actually a decent way to do this that actually provides the kind of security required these days.

    • @towerful@programming.dev
      link
      fedilink
      1
      edit-2
      9 months ago

      Is the client a web browser used by an end user? Or is it a trusted environment?

      Because if it’s a trusted environment (server to server) then you could add an extra field to your user table for api_key, and an extra tokens table to your database.
      Think of githubs legacy access token system (and, again, it’s now legacy because it’s a dated and insecure way of doing it).
      Each user gets a randomly generated 16 character string as their api_key.
      Then the user is given a way to create/regenerate/delete records into the tokens table: a friendly name, user id relation, and finally a randomly generated 16 character string as for the token.
      You could even add some sort of token expiry date, to limit the timeframe of damage for a leaked key.

      Another option for untrusted environments (egba we browser) is JWT. It’s used a lot for microservices.
      It’s a token with a lifespan minted by your Auth server. Anyone can decode the token and inspect the payload, so it’s not secure for storing passwords but it’s great for storing user ID and perhaps access scopes. The token can be verified by anyone to ensure the token is authentic and hasn’t been tampered with.
      But only servers that know the JWT secret can mint them.
      The issue with JWTs is that there is no way to revoke them. If you mint a jwt that’s valid for 4 years, the only way to invalidate it is by having all servers share a list of revoked tokens - or by having all servers call back to the Auth server that minted the token (which probably maintains a revoke list) to check it’s still valid. And, there is no way to “ban” a user if they still have a valid token.
      Essentially the JWT is keys to the kingdom, and they are hard to get back.
      Which is why they often have lifetimes of 5-30 minutes, and - you guessed it - are issued along with a refresh token.


      Edit:
      There is SRP (6a).
      https://github.com/LinusU/secure-remote-password
      All the SRP projects I can find haven’t been updated in 5 years - not a good look for a security system.

      The problem with all of these things is that they haven’t been deemed secure enough or provide enough additional benefits for widespread adoption.
      The money has gone into oauth, oidc, saml, jwt etc.

      • @mrkite@programming.dev
        link
        fedilink
        English
        19 months ago

        The issue with JWTs is that there is no way to revoke them.

        Except you can have a nonce in the JWT that corresponds to a field on the server… which is revokable.

      • @pe1ucaOP
        link
        fedilink
        19 months ago

        Oh I’ve only used JWTs with OIDC so I didn’t thought about using them directly.
        It could be a good solution since the user can generate them on their own and we can validate them with the correct information (secret or public key).

        About the issue of long lived or not expiring JWT, maybe a custom restriction of valid tokens with lifespans of more than X amount of minutes are rejected?
        Yeah, the token could be a valid one but we could say the payload is invalid for our API.

    • @noli@programming.dev
      cake
      link
      fedilink
      19 months ago

      Couldn’t you do something like JWT except allow the client to slap on their credentials to any initial request?

      From the backend side that means that if there is no valid token, you can check the request body for the credentials. If they’re not there, then it’s an unauthorized request.

      You’re eliminating a singular request in a long period of time at the cost of adding complexity to both client and backend but if the customer wants to be silly that’s their fault