Search code examples
phpapisecurityencryptionaccess-token

Creating access token by encrypting user's id + time as JSON


I'm making an API and I'm new into it. I thought of this idea for creating an access token. Basically, we encrypt JSON data with a key, turn it to Base64 and return it as the access token. Later, API decrypts that access token and can use the id. E.g.:

login method: we check if username/password is correct, if it is, then we get user's id from the database. Then we construct the access token, which will be used in the future API calls, like this:

  1. we make a JSON string, e.g. {"user_id":609, "logintime":"1430428180"}
  2. we use encrypt function(e.g. these) and encrypt that JSON string using our encryption key.
  3. we turn encrypted string to Base64, so it is readable and returns it as the access token.

Later, when the user makes an action, we can use this access token to know about the user. E.g.:

update user info method: we send data, which needs to be updated and access token, which we got earlier. Then we:

  1. Base64 decode access token.
  2. decrypt it using our encryption key.
  3. parse JSON data and we know user's id, so we can easily update the database. Also, we know API call is legit because the decrypted string was JSON and it contained user_id and logintime fields.

Is this approach any good?


Solution

  • In this answer I'm going to focus more on API security, which I feel is the real question behind the question (prompted by OPs comment on my first answer).

    The Purpose of API Keys

    API keys are used in cases where a user logging in is difficult, impossible, or otherwise undesirable. The API key is used for Authentication, the "proof of who I am" part of API access. Authorization, or the "I am allowed to do this" is usually stored on the server.

    Criteria for Valid API Access

    In order for you to be satisfied that a request is valid, you must consider a couple of points.

    1. Did the request originate from an authorized user?
    2. Has the request been tampered with in transit?

    In order to make sure point 1 holds true over time, an API should also be concerned that a secret key stays secret.

    1. This request does not damage the validity of future requests.

    Please note that although API keys are usually sent as HTTP headers, I'm going to be using GET variables in my examples for simplicity.

    Single Key API

    Some APIs are set up to give access with a single key. Let's use CEgJu8G483u3 as our example key.

    The user would send over a request like

    GET /users?apiKey=CEgJu8G483u3 HTTP/1.1
    Host: api.bigorg.com
    

    If the API is communicating over HTTP, this is inherently insecure:

    1. The key is publicly readable, (and therefore usable) by any nosy 3rd party.
    2. The 3rd party can tamper with the body of the request as much as they like, and the server is unaware.

    So in single key API authentication over HTTP, security criteria 1 - 3 violated.

    If your single key API is communicating over HTTPS only, then 1 - 3 are still valid and can be considered safe.

    Digest, or Signature APIs

    This approach is used by Google and Amazon for access to many of their APIs.

    When the server grants access to their system, two keys are generated, an identifier and a secret.

    ACCESS_IDENTIFIER = 'bill';
    ACCESS_SECRET = 'U59g5LcBBZpw';
    

    Once the two keys have been generated, the client and server store a copy of the secret, which is never directly transmitted again.

    When a request is made under this system, the identifier is provided with the request (note that the access key can be as complicated as a randomly generated string, or as simple as the id of the user trying to access the system.

    Just before the request is sent by the client, a digest of the request is generated and then encrypted using the previously agreed secret. The methods of generating the signature vary, but a common one right now is HMAC.

    The request then looks like this

         GET /users?identifier=bill&signature=JjU1j1fIH62KG7FCTdZGzK7J HTTP/1.1
         Host: api.bigorg.com
    

    Random pseudo-signature used in this example

    If the request body is included when generating the signature, you can now authenticate safely.

    Note that listening to HTTP traffic is still very very simple, so unauthorized knowledge will still be a problem, but the listening party cannot send their own requests or tamper with yours.

    If the request is tampered with, the signature becomes invalid and the request will be rejected.

    JSON Web Tokens (your solution)

    Your solution is nearly identical to JSON Web Tokens, so I'll just pretend you're using that.

    This solution is secure over HTTPS. (DO use HTTPS)

    1. The request comes from a valid user => They knew the token
    2. The request has not been tampered with => HTTPS prevents a 3rd party from tampering with a request
    3. The access key remains a secret => HTTPS prevents a 3rd party from reading your access key

    If your API is using HTTP/plaintext, you can use your JSON Web Tokens as the access key, but you'll need to also generate a private secret and use that to add a signature to the request.

    Note that I'm not sure how you'll transmit the secret to the client if you're using HTTP. Maybe via snail mail or email?

    Conclusion

    Your idea has been replicated by very smart people. Congratulations! This means it was a good idea!

    Your access key solution is solid provided that you use HTTPS.