如何隔离 Restful API 中的数据

how to isolate data in Restful API

还有一些restfulapi,如下:

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

而且,每张发票或交易都属于一个特定的帐户。

实施restfulapi时,我们必须满足:每个账户只能查看自己的发票或交易。

我们应该如何隔离 restful api 中的数据?

当然我们可以把account number传给api,比如:

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

但是Invoice Number已经能够唯一标识一个资源了。所以我不想把问题复杂化。

还有其他方法可以解决这个问题吗?

你在这里混了很多东西。

不是 REST 问题,这是一个安全问题。更准确地说,它是 OWASP top 10 2013 Insecure direct object vulnerability.

让我们简单点:你有一个像这样的URL

.../superSensitiveStuff/1

并且您想阻止“1”的所有者访问“.../superSensitiveStuff/2

据我所知,可以通过三种方式处理此问题:

  1. 在请求中强制执行完整性 URLs。此策略并不适用于所有情况,它仅适用于客户端向服务器先前通信的资源发出请求的情况。在这种情况下,服务器可能会添加这样的查询参数

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

    其中 HMAC 是 cryptographic HASH function。如果参数丢失,服务器将丢弃请求,如果存在,服务器将能够验证它是否完全是授权的 URL 因为 HMAC 值保证其完整性(有关其他信息,请点击 link 以上)。

  2. 使用不可预测的引用。这里的问题是用户可以猜测另一个 id。 “呃……我有1号资源,让我看看2号资源是否存在”。如果你放弃序列并移动到 long 随机数,这是很难做到的。资源将变为

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

    这很好,因为它既有效又便宜。

  3. 利用混合 RBAC-ABAC 方法。 RBAC 代表基于角色的访问控制,这就是您正在使用的。第二个首字母缩写词的前导 A 代表属性。这意味着访问是根据用户角色 属性提供的。在本例中是 userId,因为它必须经过身份验证才能访问私有资源。简而言之,当用户请求特定的 .../superSensitiveStuff 资源时,当您拥有该资源的所有权信息时,它会从存储库中加载。例如,它可能是一个数据库,您的 SuperSensitiveStuff java 业务模型可能是这样的

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

    现在,您可以在您的控制器中执行以下操作

    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