参考这个问题:Braintree Dropin UI 无法与 Ionic Framework 一起使用,除非强制刷新 https://stackoverflow.com/questions/32531662/braintree-dropin-ui-does-not-work-with-ionic-framework-unless-force-refresh/32571410#32571410
我当前的 Ionic / Angular / Firebase + 一个非常简单的 Node 服务器应用程序在使用 Braintree 向用户信用卡收费时存在安全问题。根据 @RaymondBerg 的说法,问题在于客户端可以发布任何 customerId 并创建 Braintree 令牌并向该客户收费。由于我所有的用户授权都发生在 Firebase / Angular - 客户端。因此,当用户从我的 AngularJS/Ionic 到我的 Node 服务器执行 $HTTP.post 时,我不想再次授权它们(因为我什至不知道如何做到这一点,所以我使用 Firebase)。
那么设置 Firebase 和我的 Node 服务器以与 Braintree 等支付系统配合使用的策略是什么?
我能想到的一件事是,首先在 http 请求之前在我的 firebase 中创建一个节点,然后在客户端(Ionic 应用程序)中传入客户端 $id 进行请求:
$scope.getToken = function () {
var ref = new Firebase('[FirebaseURL]/braintreePaymentToken');
var tokenObj = $firebaseObject(ref.child(posterId));
tokenObj.tokenGenerated = true;
tokenObj.$save().then(function(){
$http({
method: 'POST',
url: 'http://localhost:3000/api/v1/token',
data: {
//user $id from Firebase
userId: snapshot.key(),
}
})
}
在 Firebase 中,我将安全规则设置为:
"braintreePayment": {
".read": false,
".write": false,
},
"braintreePaymentToken": {
"$uid": {
".read": "auth != null",
".write": "auth != null && auth.uid == $uid",
}
},
这样,临时节点 BraintreePaymentToken 只能由应用程序中的当前登录用户写入。其他登录用户(恶意用户)不能在此节点上写入,因为他们的auth.uid将不等于posterId,其中posterId是需要付费的用户。
在服务器端,我使用一次来查看是否可以找到该值:
var ref = new Firebase('[FirebaseURL]');
app.post('/api/v1/token', jsonParser, function (request, response) {
var userId = request.body.userId;
console.log (userId);
//customerId from braintree is stored here so no one except the server can read it
ref.child('braintreePayment').child(userId).once("value", function(snapshot){
var exists = (snapshot.val() !== null);
console.log (exists);
if (exists) {
console.log ("using exsiting customer!");
//If braintreePaymentToken with userId child exsited, it mean this request is come from my Ionic client, not from anywhere else.
ref.child('braintreePaymentToken').child(userId).once("value", function(snap) {
if (snap.val()) {
gateway.clientToken.generate({
customerId: snapshot.val().customerId
}, function (err, res) {
if (err) throw err;
response.json({
"client_token": res.clientToken
});
//After I return the clientToken, I delete the braintreePaymentToken node. It is like using Firebase to send email with Zaiper. More secue I guess?
ref.child('braintreePaymentToken').child(userId).remove();
});
else {
response.json({
"client_token": "Unauthorized Access!"
});
}
} else {
console.log ("using no customer!");
gateway.clientToken.generate({}, function (err, res) {
if (err) throw err;
response.json({
"client_token": res.clientToken
});
});
}
});
});
当用户点击我的客户端(离子应用程序)上的支付按钮时,我再次执行 Firebase Once 请求以查看 customerId 是否已在我的 firebase/braintreePayment 中。如果没有,我们用 Braintree 创建的返回交易 customerId 保存一个。
app.post('/api/v1/process', jsonParser, function (request, response) {
var transaction = request.body;
ref.child('braintreePayment').child(transaction.userId).once("value", function(snapshot){
var exists = (snapshot.val() !== null);
console.log (exists);
if (exists) {
console.log ("Return customer!");
gateway.transaction.sale({
amount: transaction.amount,
paymentMethodNonce: transaction.payment_method_nonce,
options: {
submitForSettlement: true
},
}, function (err, result) {
if (err) throw err;
response.json(result);
});
} else {
console.log ("First time customer!");
gateway.transaction.sale({
amount: transaction.amount,
paymentMethodNonce: transaction.payment_method_nonce,
options: {
store_in_vault_on_success: true,
submitForSettlement: true
},
}, function (err, result) {
if (err) throw err;
console.log ("Customer Id: " + result.transaction.customer.id);
var customerId = result.transaction.customer.id;
ref.child('braintreePayment').child(transaction.userId).update({customerId: customerId});
response.json(result);
});
}
});
});
如您所见,这确实很复杂。但我不知道有更好、更安全的方法来做到这一点......
这是 Firebase、Node 和 Braintree 之间构建的最佳方式吗?这是否解决了 OWASP 安全问题?有没有办法改进这段代码,或者有更好的方法来做到这一点?
Thanks!