有一些宁静的apis,如下:
api/v1/billing/invoices/{invoiceNumber}
api/v1/billing/transactions/{transactionNumber}
并且,每张发票或交易都属于特定帐户。
在实施restful apis时,我们必须满足:每个帐户只能查看自己的发票或交易。
我们应该如何在restful apis中隔离数据?
当然,我们可以将account number
传递给api,例如:
api/v1//billing/invoices/{invoiceNumber}?accoutNumber=XXX
api/v1/billing/{accountNumber}/invoices/{invoiceNumber}
但是Invoice Number
能够唯一地识别资源。所以我不希望问题变得复杂。
还有其他方法可以解决这个问题吗?
答案 0 :(得分:3)
你在这里混合很多东西。
这是不 REST问题,这是一个安全问题。更确切地说,它是OWASP top 10 2013 Insecure direct object vulnerability。
让它变得简单:你有这样的网址
.../superSensitiveStuff/1
并且您希望阻止" 1
"的所有者从访问" .../superSensitiveStuff/2
"
据我所知,有三种方法可以解决这个问题:
在请求网址中强制执行完整性。此策略不适用于所有情况,它仅适用于客户端向先前由服务器传送的资源发出请求的情况。在这种情况下,服务器可以添加像这样的查询参数
.../superSensitiveStuff/1?sec=HMAC(.../superSensitiveStuff/1)
其中HMAC是cryptographic HASH function。如果参数丢失,服务器将丢弃请求,如果它在那里,服务器将能够验证它是否完全是授权URL,因为HMAC值保证其完整性(对于其他信息,命中上面的链接)。
使用不可预测的引用。这里的问题是用户可以猜出另一个id。 " 嗯...我的资源编号为1,让我检查资源编号是否存在"。如果你删除序列并移动到 long 随机数,这很难做到。资源将成为
.../superSensitiveStuff/195A23FR3548...32OT465
这很好,因为它有效且便宜。
利用混合RBAC-ABAC方法。 RBAC代表基于角色的访问控制,这就是您正在使用的。第二个首字母缩写词的前导A代表属性。这意味着基于用户角色和属性提供访问权限。在这种情况下是userId,因为它必须经过身份验证才能访问私有资源。简而言之,当用户请求特定的.../superSensitiveStuff
资源时,如果您拥有该资源的所有权信息,则会从存储库中加载该资源。例如,它可能是一个DB,您的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