title: Firebase Realtime database was manipulated
body: <p>I am using firebase realtime database for a money transfer app where a user creates an account with a unique username and gets to send/receive money. The backend just subtracts numbers from one account and adds to another account. Everything worked fine until an attacker created about 3 accounts. Then added money to one legally but then sent the same amount to the other two accounts at the same time. For context, He created account A,B,C, added $10 to 'A' legally, then sent that $10 to 'B' &amp; 'C' at the same time making 'B' &amp; 'C' have $10 each. Then repeated the process allover.</p>
  <p>Each transaction has a timestamp but the difference between the two transactions was just milliseconds apart which seems like it was in a forloop and could have been executed by a human cause of delay.</p>
  <p><em><strong>How my database is structured</strong></em></p>
  <p><a href="https://i.stack.imgur.com/dIosz.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/dIosz.png" alt="enter image description here" /></a></p>
  <p><em><strong>The transaction dates between the two shows just milliseconds apart which shouldn't be possible sending to two different accounts</strong></em>
  <a href="https://i.stack.imgur.com/PVcqf.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/PVcqf.png" alt="enter image description here" /></a></p>
  <p><em><strong>Below is my kotlin code</strong></em></p>
  <pre><code>override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.pay_activity_review_local)
  
      ref  = Firebase.database.getReference(&quot;UsersAccount&quot;)
      progressHud = SVProgressHUD(this)
  
      nameTV.text = recipientFullName
      tagTV.text = userTag
      currencyCodeTV.text = recipientCurrencyCode
      amountTV.text = &quot;${CurrentUser.currencySymbol}${sendReceiveAmount.commaRepresentation()}&quot;
      swipeBtn.setText(&quot;Swipe to $sendRequestText&quot;)
  
      swipeBtn.setOnStateChangeListener {
  
          if (it) {
  
             sendBtnPressed()
          } 
      }
  }
  
  private fun sendBtnPressed() {
  
      val valueEventListener = object : ValueEventListener {
  
          override fun onDataChange(dataSnapshot: DataSnapshot) {
  
              if (dataSnapshot.exists()) {
  
                  currentUserBalance = dataSnapshot.child(&quot;AvailableAmount&quot;).getValue(Double::class.java)
                  Log.i(&quot;xxx currentUserBalance&quot;, &quot;$currentUserBalance&quot;)
  
                  val desc = descriptionET.text.trim().toString()
  
                  if (desc.isNotEmpty()) {
  
                      if (isRequest) {
                          // issa request
                          generateRequest(recipientId, desc, isRequest, sendReceiveAmount)
  
                      } else {
                          // issa send
                          if (currentUserBalance!! &gt;= sendReceiveAmount) {
  
                              sendAmountToRecipientAccount(sendReceiveAmount, recipientId, desc)
  
                          } else {
                              // insufficient
                              progressHud.dismissImmediately()
                              extensions.infoAlertDialog(this@ReviewLocalActivity, &quot;Why?&quot;, &quot;Your funds are insufficient, deposit funds in your wallet to continue&quot;, false)
                          }
                      }
  
                  } else {
                      // description is empty
                      progressHud.dismissImmediately()
                      extensions.infoAlertDialog(this@ReviewLocalActivity, &quot;Description field empty&quot;, &quot;Enter description of payment&quot;, false)
                  }
  
              } else {
                  return
              }
          }
  
          override fun onCancelled(databaseError: DatabaseError) {
              // Getting Post failed, log a message
              Log.i(&quot;xxx onCan&quot;, databaseError.toException().toString())
          }
      }
  
      ref = Firebase.database.getReference(&quot;UsersAccount&quot;)
      ref.child(CurrentUser.userId).addListenerForSingleValueEvent(valueEventListener) 
  }
  
  private fun sendAmountToRecipientAccount(amountToSend: Double, recipientId: String, desc: String) {
  
      progressHud.showWithStatus(&quot;&quot;)
  
      val valueEventListener = object : ValueEventListener {
          override fun onDataChange(snapshot: DataSnapshot) {
  
              if (snapshot.exists()) {
  
                  val recipientBalance = snapshot.child(&quot;AvailableAmount&quot;).getValue(Double::class.java)!!
  
                  //add amount to recipient current account
                  val newBalance = recipientBalance + amountToSend
  
                  val values: MutableMap&lt;String, Any&gt; = HashMap()
                  values[&quot;AvailableAmount&quot;] = newBalance
  
                  ref.child(recipientId).updateChildren(values).addOnSuccessListener {
  
                      // debiting current user account
                      val amountToDebit = currentUserBalance!! - amountToSend //force unwrap cos value def exists
  
                      val values2: MutableMap&lt;String, Any&gt; = HashMap()
                      values2[&quot;AvailableAmount&quot;] = amountToDebit
  
                      ref.child(CurrentUser.userId).updateChildren(values2).addOnSuccessListener {
  
                          //sending push notification to recipient
                          val notifyObject = JSONObject()
                          val notifyInfo = JSONObject()
  
                          notifyInfo.put(&quot;title&quot;,&quot;Credit&quot;)
                          notifyInfo.put(&quot;body&quot;,&quot;${CurrentUser.firstName} sent you ${CurrentUser.currencySymbol}${amountToSend.commaRepresentation()}.&quot;)
                          notifyInfo.put(&quot;badge&quot;,1)
                          notifyInfo.put(&quot;sound&quot;,&quot;default&quot;)
  
                          notifyObject.put(&quot;to&quot;, recipientRegToken)
                          notifyObject.put(&quot;notification&quot;, notifyInfo)
  
                          extensions.sendFirebasePushNotification(notifyObject, this@ReviewLocalActivity)
  
                          // creating transaction receipt
                          generateTransactionReceipt(recipientId, desc, isRequest, amountToSend)
  
                      }.addOnFailureListener {
  
                          Log.e(&quot;xxx firebase&quot;, &quot;Error getting data&quot;, it)
                      }
  
                  }.addOnFailureListener {
  
                      Log.e(&quot;xxx firebase&quot;, &quot;Error getting data&quot;, it)
                  }
              }
          }
  
          override fun onCancelled(error: DatabaseError) {
  
              Log.i(&quot;xxx onCancelled&quot;, error.message)
          }
  
      }
      ref.child(recipientId).addListenerForSingleValueEvent(valueEventListener)
  }
  
  private fun generateTransactionReceipt(recipientId: String, desc: String, isRequest: Boolean, amount: Double) {
  
      val currentUserFullName = &quot;${CurrentUser.firstName} ${CurrentUser.lastName}&quot;
      val transactionDate = ServerValue.TIMESTAMP
      val transactionId = ref.child(CurrentUser.userId).push().key
  
      // updating currentUser transaction history
      val values: MutableMap&lt;String, Any&gt; = HashMap()
      values[&quot;TransactionDate&quot;] = transactionDate
      values[&quot;SenderId&quot;] = recipientId
      values[&quot;SenderName&quot;] = recipientFullName // recipientName bcos historyActivity will display it for current user
      values[&quot;SenderEmail&quot;] = recipientEmail
      values[&quot;Description&quot;] = desc
      values[&quot;ImageUrl&quot;] = recipientImgUrl
      values[&quot;Amount&quot;] = amount
      values[&quot;CurrencyCode&quot;] = recipientCurrencyCode
      values[&quot;IsRequest&quot;] = isRequest
      values[&quot;IsCredit&quot;] = false
      values[&quot;IsWithdraw&quot;] = false
      values[&quot;IsDeclined&quot;] = false
  
      ref.child(CurrentUser.userId).child(&quot;TransactionHistory&quot;).child(&quot;$transactionId&quot;).updateChildren(values)
              .addOnSuccessListener {
  
                  extensions.claimFirstTimeBonus(CurrentUser.userId) //claiming user first time bonus
  
                  // updating recipient transaction history
                  val values2: MutableMap&lt;String, Any&gt; = HashMap()
                  values2[&quot;TransactionDate&quot;] = transactionDate
                  values2[&quot;SenderId&quot;] = CurrentUser.userId
                  values2[&quot;SenderName&quot;] = currentUserFullName
                  values2[&quot;SenderEmail&quot;] = CurrentUser.email
                  values2[&quot;Description&quot;] = desc
                  values2[&quot;ImageUrl&quot;] = CurrentUser.imageUrl
                  values2[&quot;Amount&quot;] = amount
                  values2[&quot;CurrencyCode&quot;] = CurrentUser.currencyCode
                  values2[&quot;IsRequest&quot;] = isRequest // will be true
                  values2[&quot;IsCredit&quot;] = true
                  values2[&quot;IsWithdraw&quot;] = false
                  values2[&quot;IsDeclined&quot;] = false
  
                  ref.child(recipientId).child(&quot;TransactionHistory&quot;).child(&quot;$transactionId&quot;).updateChildren(values2)
                          .addOnSuccessListener {
  
                              progressHud.dismissImmediately()
  
                              val intent = Intent(this, SentActivity::class.java)
  
                              intent.putExtra(&quot;senderAmount&quot;, amount)
                              intent.putExtra(&quot;senderCurrCode&quot;, CurrentUser.currencyCode) // both recip/sender have same code for local transaction in Sent Activity
                              intent.putExtra(&quot;recipientCurrCode&quot;, recipientCurrencyCode)
                              intent.putExtra(&quot;sendOrRequestText&quot;, sendRequestText)
                              intent.putExtra(&quot;recipientImgUrl&quot;, recipientImgUrl)
                              intent.putExtra(&quot;recipientFullName&quot;, recipientFullName)
                              intent.putExtra(&quot;isRequest&quot;, isRequest)
                              intent.putExtra(&quot;isVerified&quot;, isVerified)
                              this.startActivity(intent)
  
                          }.addOnFailureListener {
  
                              Log.e(&quot;xxx firebase&quot;, &quot;Error getting data&quot;, it)
                          }
  
              }.addOnFailureListener {
  
                  Log.e(&quot;xxx firebase&quot;, &quot;Error getting data&quot;, it)
              }
  }
  
  private fun generateRequest(recipientId: String,
                              desc: String,
                              isRequest: Boolean,
                              amount: Double) {
  
      progressHud.showWithStatus(&quot;&quot;)
  
      val currentUserFullName = &quot;${CurrentUser.firstName} ${CurrentUser.lastName}&quot;
      val requestDate = ServerValue.TIMESTAMP
      val transactionId = ref.child(CurrentUser.userId).push().key
  
      // goes to recipient &amp; checks if current userId isBlocked by recipient
      ref.child(recipientId).child(&quot;BlockedLists&quot;).child(CurrentUser.userId).get().addOnSuccessListener {
  
          val isBlocked = it.exists()
  
          if (isBlocked) {
  
              progressHud.dismissImmediately()
              extensions.infoAlertDialog(this, &quot;Blocked&quot;, &quot;You have been blocked by this user.&quot;, false)
  
          } else {
  
              // generating currentUser request
              val values: MutableMap&lt;String, Any&gt; = HashMap()
              values[&quot;TransactionDate&quot;] = requestDate
              values[&quot;SenderId&quot;] = recipientId
              values[&quot;SenderName&quot;] = recipientFullName // recipientName bcos historyActivity will display it for current user
              values[&quot;SenderEmail&quot;] = recipientEmail
              values[&quot;Description&quot;] = desc
              values[&quot;ImageUrl&quot;] = recipientImgUrl
              values[&quot;Amount&quot;] = amount
              values[&quot;CurrencyCode&quot;] = recipientCurrencyCode
              values[&quot;IsRequest&quot;] = isRequest // will be true
              values[&quot;IsCredit&quot;] = false
              values[&quot;IsWithdraw&quot;] = false
              values[&quot;IsDeclined&quot;] = false
  
              ref.child(CurrentUser.userId).child(&quot;TransactionHistory&quot;).child(&quot;$transactionId&quot;).updateChildren(values)
                  .addOnSuccessListener {
  
                  //sending push notification to recipient
                  val notifyObject = JSONObject()
                  val notifyInfo = JSONObject()
  
                  notifyInfo.put(&quot;title&quot;,&quot;Request&quot;)
                  notifyInfo.put(&quot;body&quot;,&quot;${CurrentUser.firstName} requested ${CurrentUser.currencySymbol}${amount.commaRepresentation()} from you.&quot;)
                  notifyInfo.put(&quot;badge&quot;,1)
                  notifyInfo.put(&quot;sound&quot;,&quot;default&quot;)
  
                  notifyObject.put(&quot;to&quot;, recipientRegToken)
                  notifyObject.put(&quot;notification&quot;, notifyInfo)
  
                  extensions.sendFirebasePushNotification(notifyObject, this)
  
                  // generating recipient transaction history
                  val values2: MutableMap&lt;String, Any&gt; = HashMap()
                      values2[&quot;TransactionDate&quot;] = requestDate
                      values2[&quot;SenderId&quot;] = CurrentUser.userId
                      values2[&quot;SenderName&quot;] = currentUserFullName
                      values2[&quot;SenderEmail&quot;] = CurrentUser.email
                      values2[&quot;Description&quot;] = desc
                      values2[&quot;ImageUrl&quot;] = CurrentUser.imageUrl
                      values2[&quot;Amount&quot;] = amount
                      values2[&quot;CurrencyCode&quot;] = CurrentUser.currencyCode
                      values2[&quot;IsRequest&quot;] = isRequest // will be true
                      values2[&quot;IsCredit&quot;] = true
                      values2[&quot;IsWithdraw&quot;] = false
                      values2[&quot;IsDeclined&quot;] = false
                      values2[&quot;IsVerified&quot;] = isVerified
  
                  ref.child(recipientId).child(&quot;TransactionHistory&quot;).child(&quot;$transactionId&quot;).updateChildren(values2)
                      .addOnSuccessListener {
  
                          progressHud.dismissImmediately()
  
                          val intent = Intent(this, SentActivity::class.java)
  
                          intent.putExtra(&quot;senderAmount&quot;, sendReceiveAmount)
                          intent.putExtra(&quot;recipientCurrCode&quot;, recipientCurrencyCode)
                          intent.putExtra(&quot;sendOrRequestText&quot;, sendRequestText)
                          intent.putExtra(&quot;recipientImgUrl&quot;, recipientImgUrl)
                          intent.putExtra(&quot;recipientFullName&quot;, recipientFullName)
                          intent.putExtra(&quot;isRequest&quot;, isRequest)
                          intent.putExtra(&quot;isVerified&quot;, isVerified)
                          this.startActivity(intent)
  
  
                      }.addOnFailureListener {
                          Log.i(&quot;xxx firebase&quot;, &quot;Error getting data&quot;, it)
                      }
  
                  }
                  .addOnFailureListener {
                      Log.i(&quot;xxx firebase&quot;, &quot;Error getting data&quot;, it)
                  }
          }
  
      }.addOnFailureListener {
  
          Log.i(&quot;xxx firebase&quot;, &quot;Error getting data&quot;, it)
      }
    }
  }
  </code></pre>
  <p>My last solution was coming here as I have been trying for a while. Thank you so much for taking your time out to help. For further clarification please leave a comment</p>