MySQL/PHP request manipulation - php

First time posting.
I manage a website that handles certain types of transactions for virtual currency.
It is a php/mysql web application.
Recently we've had a user somehow withdraw the same amount (essentially duplicating their virtual money) 6 or 7 times (until we ran out of funds).
Looking at the log, the transactions were processed milliseconds apart, so I'm assuming that they user had for example 5,000 funds and requested to withdraw them by spamming the request in order to attempt to withdraw more than they owned.
How could I go about preventing this from happening in the future, and how could I test this, or repeat this process myself?
Thanks for any help.
I don't think this is a typical question on here, I'm sorry. I'm not a developer, my current developer is on leave, so he's refused to assist.

One way this could be managed would be to force a certain time interval to pass at a timestamp level (i.e the user cannot process multiple transactions within x minutes).
To do this, and assuming you'd be inserting into your table the transaction stats then apply it accordingly, you can force a check constraint on new insertions that will reject any row not respecting your timestamp condition

A SQL to withdraw funds should be:
UPDATE balance
FROM users
SET balance = balance - $amount
WHERE id = $id AND balance >= $amount
If there is 0 rows affected then there is insufficient balance.
Any solution that tests balance before hand is susceptible to race conditions.

Some idea to avoid to spam request :
FRONT SIDE :
Disable the button that allow user to click and spam the request.
When user click replace the button by some loading icon
BACK SIDE :
Create a temporary file when the transaction begin (or edit one file to add some information about the transaction) and when the transaction end delete the file (or the data). So before EACH transaction, you check if you have the file / data : if you have something -> no transaction because one is already running. Some documentation about this : http://php.net/manual/fr/function.file-put-contents.php
An other idea is to add some param in your database (or create some table like user_transaction) and when you start the transaction you create a user_transaction row (or change a param from 0 to 1 as you want) and when the transaction end you delete the row or change the param from 1 to0. So before EACH transaction you check if a row exist for this user or if the param is 1 : if yes -> no transaction because one is already running. Nothing complicated here, I have no information about your database so can't do more :)
Try to add one lock from FRONT + one lock from BACK and you should reduce the problem !
EDIT : add some tracking
To avoid user to spam request, you can add some tracking to avoid fraude :
You can add some counter and add +1 each time user click to know if he spam
You can create a table in database to know each time a user send a request to know which user click, when and how much currency for example
This way you know who fraude (who click a lot to spam request or who send to many request in few times) and you can send email or warning message for example, or if you can track the amount of currency he win by "cheating", ask him to give it back I don't know how your app works !

If the type of the balance is an integer or big integer, making it unsigned will prevent it going negative.
ALTER TABLE user MODIFY balance BIGINT UNSIGNED NOT NULL;

Related

How to handle same time request with Apache and MySQL [duplicate]

I am writing a hotel booking software using PHP and MySQL. I am pretty much done with it but I ran into a race condition problem. For example there is only one standard room left and when 2 guests both select it it shows available to both of them. I tried fixing it by checking the room again when the guest clicks confirm before payment but that still has issues. I also tried making the room status to pending when whoever clicks the confirm first but I can't figure out how to change it back to available if the guest decides not to pay or just closes the browser. I searched SO for answers but I didn't really find a definitive answer. Thanks in advance
One solution is to add two columns to a table in the database. One column is the session ID or user ID or whatever of the user that is being offered the room. The second column is a timestamp indicating when that offer will expire.
Then, in your app, only show rooms that have an expired timestamp in the hold column. (Set the initial timestamp to 0 so that it starts out expired.) When a room is selected, check the column again. If there's an unexpired timestamp there, the user gets a "sorry, you were too slow" message. Otherwise, put a timestamp there for 15 minutes into the future or whatever, and proceed.
You see this on travel sites and ticket-purchasing sites a lot where it says something like "We're holding these seats for you for another 14 minutes. Please complete the transaction by then or it will be released blah blah blah."
I would also go with the pending state. You could save the state together with the session id of this user and have a cronjob that deletes all pending states that have expired session ids associated to them.

How to assign a record to a person from a pool automatically using MySQL/PHP while preventing across assignment

I have a script that is written in PHP. It uses MySQL database to store records.
Basically, I have team of users that are making random calls to a different business. I want to add list of phone number in a queue "pool table". The system will need to assign the new call to the user. Now If a user is already working on a phone call I don't want another user to start calling the same number. I need a solution to prevent 2 people having the same record assigned to them. So if phone number 000-000-0000 is assigned to the user X the same record will be skipped and the next one in line get assigned to the next available user.
This table will be accessed a lot so I need a good solution that will prevent 2 people from working on the same record and also not cause system issues.
One way I can think of but looking for a better solution is
open transaction
select a call where record status is available
update that call by changing the status from records available to record pending.
commit transaction.
If the use completed the call then updated with a status of completed otherwise make the record available again.
what are other solution available for me?
Thanks
Without a little more information about the workflow, it's hard to know what to suggest, but it sounds like users are interacting with the application somehow while they are taking calls...true??
If so, you must have some way for the user to alert the system they are ready for a call.
ie...
I just started my shift... Deal me a number.
Or...
Submit notes from last call... click submit and Deal me another number.
In this scenario, it seems like it should pretty easy to just let the users "request" the next number. You could probably just insert the users id on that record so it shows in their queue.

How do I prevent people from opening a record if someone else has it open?

I'm creating a PHP Web application, which would involve:
1) Users opening a record
2) Users making changes to the record
3) Saving changes to the record
Since this is a multi-user application, I want to prevent situations where two users have the same record open at the same time, and one user's changes overwrites the next, preferrably by enforcing some sort of locking method when a record is opened that automatically unlocks when the user navigates away from the page.
By record, you mean SQL records? If so, you could add another column isOpen. Set it to 1 as long as someone else has it open, and in that case, do not serve it to anyone else.
In situations like this, it works best to also implement a timeout mechanism, where a record can be open only for 'x' min before being forcibly closed.
(Edit: This answer is assuming you want to keep a record locked the entire duration a user is viewing the info fetched from the table. If you want to lock a record only for the instant that a read/write operation is occuring on that record, MySQL engines have inbuilt mechanisms for that)
In response to your comment
To make a record accessible to others when the active user navigates away, off the top of my head, I can think of two ways to achieve it:
Allow the timeout mechanism to take care of it. Depending on your scenario, a short enough time window could work fine.
In addition to the timeout, also implement a heartbeat mechanism - an Ajax script on the page polls the server letting it know the page is still open. If the user navigates away, the server recognizes the skipped heartbeat, and unsets the record. In this case, the timeout would still take precedence. So, if the user leaves the window open and walks away, the server would still receive the heartbeat, but when the time window closes, the server unsets the record (despite still receiving heartbeats).
I use a field update_date. When user reads the record I write a cookie with this date. When user updates the record and submits the new data I'am adding WHERE update_date = '$my_escaped_date' AND id = '$the_edited_id' and if mysql_affected_rows is zero I'm showing error message that the edited data is old. It's not perfect as if you edit old data you must reenter it, but it does the job.
A locking method is exactly what is available in mysql:
http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
It's not automatic but it allows you to lock a table, do stuff and then unlock it again.
Be carefull tho' that the system does not become locked up if you forget to unlock a table or a user takes a long time to change something and you only unlock it when that user submits the form.
A better way might be to read data from the table and upon submission of the form, check to see if the data has not been altered. If it has you can notify the user of the changes and other wise you can lock the table, perform the changes and unlock it again.
You can add a field in_use to the records table,
when a user open that record update its value to 1 and when he saves it
update it back to 0.
If the value is 1 - the record is locked and won't be opened for other users.

alert who is already reading this page in php?

We have a back end application to manage messages from our clients. We have 4 customer care executives and we want to prevent the situation where the same message can't be opened by two different members, so we would like to do following...
Suppose user1 opened message id 15 and after that user2 opens same message, so we would like to give a alert that 'This message is already opened by user1'. How do we do it?
Create a different table in your database.
When a user opens a message, update the table to show which message has been opened and by which user.
When another user tries to open it, crosscheck the table to see if there is a row for that message. You can then do the appropriate action such as open or warn the user.
You can delete the rows after a given timeout period to allow others to open.
Schema eg
User_id msg_id time_opened
Unfortunately, you can't use sessions since the sesssion is user specific. However, you can employ flatfiles.
To delete the rows, employ a method such as
$timeout_time_in_seconds = 30;
$time = time() - $timeout_time_in_seconds;
$Query= "delete from table where time_opened
Note that depending on the time field, which can be an int, datetime or timestring, additional date formating of the $time variable may be required. However, int will be most convenient due to ease in comparison and subtraction and no formatting.
I'm mobile so pardon any errors. Also that's why I didn't comment but had to edit. Js issues.
What happens is, when the first user clicks, a quick check and update of the database is made.
When the second user tries, the script will detect the first user has already opened by checking the database.
You can count on this to work if the traffic load is low and the number of users trying to access is not too great. And also counting on the fact that the read and insert queries occur in a short time which as you can Guess is faster then two users clicking at the same time. Unless you have another issue, this should work
Simpliest way would be to implement as pessimistic locking at the DB level
http://www.blackwasp.co.uk/PessimisticLocking.aspx
Whatever language you are using should let you check the DB to see if a row is locked or not and send a message on the screen.
You can additionally setup your application with long polling to notify users when the request resource has become available. http://en.wikipedia.org/wiki/Push_technology

slot booking problem

I am making a doctor appointment slot booking mechanism,where in doctor appointment slots will be divided into 30 mins slot each...i have achieved all the working code.. 1 problem i am facing is that..this booking is made at 2 places i.e 2 receptions..so when 1 selects a slot(radio button) not yet confirmed and saved in DB.other reception must not be able to select .how do i do it.any help on this...how do i go abt it.
This is a case of accessing "shared data". You'll need thread to make sure that only one thread has access to the data at a time to ensure it's integrity. The following might provide some ideas
http://www.alternateinterior.com/2007/05/communicating-with-threads-in-php.html
I would use some AJAX/AJAJ functions to periodically refresh data about free slots, or I would do that much more simple - when saving the appointment, just check it, if the slot will be taken, your app redirects user back to form to choose another slot.
In essence,
I will just ask for the slot timings upfront and the remaining details later.
In case the slot is available, it sends a request to the server to lock it, so that the other client cannot use it.
In case, it is not available, it will receive a small notification that this slot is unavailable, click to see available slots.
I would go with AJAX (if this is a web app). This is a distributed systems problem which resembles Blue Army - White Army problem.
Add a "Locked By" field to the table.
When booking a slot, do something like:
UPDATE tablename
SET LockedBy = userid, ...
WHERE LockedBy IS NULL
After the update, you can select to see if LockedBy is set to your userid.
If not, then someone else must have beaten you to the punch, and you need to tell the user to pick a different slot.

Categories