如何隔离 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
”
据我所知,可以通过三种方式处理此问题:
在请求中强制执行完整性 URLs。此策略并不适用于所有情况,它仅适用于客户端向服务器先前通信的资源发出请求的情况。在这种情况下,服务器可能会添加这样的查询参数
.../superSensitiveStuff/1?sec=HMAC(.../superSensitiveStuff/1)
其中 HMAC 是 cryptographic HASH function。如果参数丢失,服务器将丢弃请求,如果存在,服务器将能够验证它是否完全是授权的 URL 因为 HMAC 值保证其完整性(有关其他信息,请点击 link 以上)。
使用不可预测的引用。这里的问题是用户可以猜测另一个 id。 “呃……我有1号资源,让我看看2号资源是否存在”。如果你放弃序列并移动到 long 随机数,这是很难做到的。资源将变为
.../superSensitiveStuff/195A23FR3548...32OT465
这很好,因为它既有效又便宜。
利用混合 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
还有一些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
”
据我所知,可以通过三种方式处理此问题:
在请求中强制执行完整性 URLs。此策略并不适用于所有情况,它仅适用于客户端向服务器先前通信的资源发出请求的情况。在这种情况下,服务器可能会添加这样的查询参数
.../superSensitiveStuff/1?sec=HMAC(.../superSensitiveStuff/1)
其中 HMAC 是 cryptographic HASH function。如果参数丢失,服务器将丢弃请求,如果存在,服务器将能够验证它是否完全是授权的 URL 因为 HMAC 值保证其完整性(有关其他信息,请点击 link 以上)。
使用不可预测的引用。这里的问题是用户可以猜测另一个 id。 “呃……我有1号资源,让我看看2号资源是否存在”。如果你放弃序列并移动到 long 随机数,这是很难做到的。资源将变为
.../superSensitiveStuff/195A23FR3548...32OT465
这很好,因为它既有效又便宜。
利用混合 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