Search code examples
restrestful-authenticationrestful-architecturerestful-url

how to isolate data in Restful API


There are some restful apis, as follows:

api/v1/billing/invoices/{invoiceNumber}
api/v1/billing/transactions/{transactionNumber}

And, each invoice or transaction belong to a specific account.

When implementing the restful apis, we must meet: Each account can only view their own invoice or transaction.

How should we isolate the data in restful apis?

Of course, we can pass the account number to the api, such as:

api/v1//billing/invoices/{invoiceNumber}?accoutNumber=XXX
api/v1/billing/{accountNumber}/invoices/{invoiceNumber}

But the Invoice Number has been able to uniquely identify a resource. So I do not want the problem to be complicated.

Is there any other way to solve this problem?


Solution

  • You are mixing a lot of things here.

    This is not a REST problem, this is a security problem. More precisely, it's a OWASP top 10 2013 Insecure direct object vulnerability.

    Let's make it simple: you have a URL like this

    .../superSensitiveStuff/1
    

    and you want to prevent the owner of "1" from accessing to ".../superSensitiveStuff/2"

    To the best of my knowledge, there are three ways of dealing with this issue:

    1. enforcing integrity in request URLs. This strategy does not apply to all cases, it only works in those scenarios where the client issues a request to a resource previously communicated by the server. In this case, the server may add a query param like this

      .../superSensitiveStuff/1?sec=HMAC(.../superSensitiveStuff/1)

      where HMAC is a cryptographic HASH function. If the parameter is missing, the server will drop the request and if it's there the server will be able to verify that it's exactly the authorized URL because the HMAC value guarantees its integrity(for additional infos, hit the link above).

    2. using unpredictable references. The problem here is that a user can guess another id. "uhmm... I have the resource number 1, let me check whether the resource number 2 exists". If you drop sequences and move to long random number this is very hard to do. The resource will become

      .../superSensitiveStuff/195A23FR3548...32OT465

      This is good because it's effective and cheap.

    3. exploiting a mixed RBAC-ABAC approach. RBAC stands for Role Based Access Control and this is what you are using. The leading A of the second acronym stands for Attribute. This means that access is provided on the basis of a user role and an attribute. In this case is the userId, since it must be authenticated for accessing private resources. In few words, when a user requests a specific .../superSensitiveStuff resource it is loaded from the repository when you have the ownership information for that resource. It could be a DB, for example, and your SuperSensitiveStuff java business model could be like this

      public class SuperSensitiveStuff {
      
          private String userId;
          private String secretStuff;
          ...
      }
      

      now, in your controller you can do the following

      String principal = getPrincipal(); //you request the logged userId
      SuperSensitiveStuff resource = myService.load(id); //you load the resource using the {id} in the request path
      if (resource.getUserId.equals(principal))
          return resource //200 ok, this is an authorized access
      else
          throw new EvilAttemptException() //401 unauthorized, cheater detected