Обратитесь к этому вопросу: >Пользовательский интерфейс Braintree Dropin не работает с Ionic Framework без принудительного обновления
Мое текущее Ionic/Angular/Firebase + очень простое серверное приложение Node имеет проблему безопасности при использовании Braintree для списания средств с кредитной карты пользователя. Проблема, по словам @RaymondBerg, заключается в том, что клиент может опубликовать любой идентификатор клиента, создать токен Braintree и взимать плату с этого клиента. Поскольку вся моя авторизация пользователя происходила в Firebase/Angular — на стороне клиента. Поэтому, когда пользователь делает $HTTP.post с моего AngularJS/Ionic на мой сервер Node, я не хочу их снова авторизовать (поскольку я даже не знаю, как это сделать, поэтому я использую Firebase).
Итак, какова стратегия настройки Firebase и моего сервера Node для работы с платежной системой, такой как braintree?
Одна вещь, которую я могу придумать, это сначала создать узел в моей базе данных перед http-запросом, а затем передать клиентский $id для запроса на стороне клиента (приложение Ionic):
$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, чтобы увидеть, уже ли идентификатор клиента в моем firebase/braintreePayment. Если нет, мы сохраняем один с идентификатором клиента транзакции возврата, созданным braintree.
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? Есть ли способ улучшить этот код, чтобы он был лучше, или есть лучший способ сделать это?
Спасибо!