I have the following code snippet to delete records from a database given a primary key. This is called via an AJAX request, through GET. Anyone who were to examine my JavaScript could work out the URL and delete arbitrary records. What can I do to prevent this?
Not use GET?
Use sessions?
if($cmd=="deleterec") {
$deleteQuery = "DELETE FROM AUCTIONS1 WHERE ARTICLE_NO = ?";
if ($delRecord = $con->prepare($deleteQuery)) {
$delRecord->bind_param("s", $pk);
$delRecord->execute();
$delRecord->close();
echo "true";
} else {
echo "false";
}
}
Not using GET won't change a thing. You have to use sessions to manage 'access control' before you even get to this point in the code.
That is to say, when the AJAX request is made you should at that point confirm via the session that the person making the request is allowed to do the delete, before actually doing it.
Think about how you would check permissions in a 'non-AJAX' way and do the same here.
You can give your records a non-sequential random unique id to use in AJAX requests.
Still not the most secure solution although should be easy to integrate into your current system.
Use post rather than get
Embed hidden unique ids in any forms that will perform this action, and check that the id that comes back has been issued by you
Obfuscate the URL/parameter names
Generate unique IDs for the rows, and dont expose them externally, so an attacker cannot pick a row to delete.
Well, if not everyone can delete a record, make sure this one is behind a password, so only the authorized users can do deletes. Make yourself a login that adds a 'Delete' button on the page afterwards and on the PHP side make sure the user is authorized when processing the commands.
Oh and yes, make sure you always pass a confirmation value through POST to validate, not just a GET parameter, it's best practice, even with authorized users.
There are two important aspect to your question.
Are authorized user supposed to be able to delete any record they see fit? If that's the case your issue is one of making sure the request came from an authorized user. In that case using PHP's session is one way of making sure that it's the user you authenticated that made the request. Of course, with all technologies you have to make sure you use PHP session the right way. There are a bunch of articles on that mather. PHPsec.org has one here.
If a user cannot delete any row, in addition to doing authentication and sessions you'll also need to had a check in your code for the verify if the record being deleted can be deleted by that user. Now this is highly dependant on your application. For example, in a project management software, one could be allowed to delete tasks associated with one project but not with another one. That's part of your business logic.
The best would be to perform both check. One, your application must verify if the user making the deletion is a valid user and is allowed to delete (very simple to implement with php sessions) and two make sure that the user can delete the specific record based on some form of ownership.
Don't expose CRUD methods via Ajax.
You have to check before deleting records if user is authorized and allowed to delete.
Related
I'm trying to protect my backend from multiple post requests to avoid duplicate data on the database and the server overload.
I've already blocked the frontend disabling the submit button after a first click, but it will not prevent some "smart user" to submit my form from the console or disable javascript from the page and try something.
So I want to know if Laravel has some solution for this case.
PS: I've already tried some solutions on the backend too if you want I can post here.
As requested:
So one of my alternatives is check if the incoming data is already on the database and denied the request if is it, it will prevent the duplicate data but not the server overload.
Another alternative is to create a token in session for one use only on Create() method from the controller, send the token to the view and put it on a hidden field, retrieve it from post request and check the post token with the session token. If the two tokens are the same, then unset it to avoid others requests try using it and if they try I deny the request.
If you know who your users are (because they have an ID) then this is somewhat easy to do. Possibly use some sort of quick-access system such as Reddis to check-in that a user is in a state of edit while the action is being carried out.
However that creates complications of its own. Also doesn’t work if you don’t know who your users are.
The safer thing would be to make sure that your requests can handle potential problems. Consider using database transactions to ensure the integrity of the data.
It would depend really on what you’re trying to avoid, why you are, and what kind of data you’re worried about duplicating.
if it's too important for you to protect for multiple submit u can put a random string in hidden input in your form and also put it in a special session for each form you have and check it every time and after that try to change your session after each time you end your proccess
I am wondering about the security of my application. I'm simply using twig loop to display all of my records in database. And everything is all right when I make separate site to display the details and there are buttons to e.g. delete this thing. It usually happens DELETE method and somebody can display only own details. But I want to have button to delete specific record in basic view where every records displayed e.g. next to title of item.
I cant do this by CreateFormBuilder because I cannot send the current id of the item from the form (or I just don't know how to do it). But is it secure? Everyone can change the id parameter of button and delete other record.
I can use AJAX and simply button in twig but this is the same case. Everyone can change e.g. data-id parameter in button and delete other record.
What I should to do in this situation? How you usually solve this problem?
Summarising I want to make a secure button to delete item next to each displayed record.
Have a nice day!
One of the way to secure AJAX routes is to use JWT (json-Web-Token) (see this : https://jwt.io/) instead of random string token. This token is crypted with the user's information to ensure the person who clicked on the button is allowed to do someting (and the token is send with the request in header of the request).
Anyway, you have to send something to identify the id of the element you want to erase or modify in your database. I would personnaly implement this JWT system, but the access to the page where you can see and click on these buttons must be protected too. This way, you can assume that the users don't have bad intentions.
Theis bundle (https://github.com/lexik/LexikJWTAuthenticationBundle/blob/master/Resources/doc/index.md#getting-started) can help you to implement this on symfony very easily (i'm new to this framework and i did this in a few hours - just a thing: if you use Apache, don't forget to allow override in your Apache configurations to allow the .htaccess of symfony to do his job, or headers will be strip by Apache - it makes me several hours to find why things did not work !).
Hope this will help !
It really boils down to what kind of app you want to build:
more traditional app - with full page refresh/navigation cycle
AJAX based - with all sorts of async calls to your server
In any case you choose, it is your responsibility to check whether the current user is authorized to make any change (deletion included) to an object. This comes as especially important in case of id being integer, which is predictable.
So, first, make sure your routes are protected from anonymous users, and second, make sure that you put in place permission strategy which allows/denies user's specific action.
Most of the permission-related stuff can be achived via Voters, but if you are really in need of heavy-lifting you can turn to ACL.
Hope this helps...
I am trying to determine the safest way or best practice to go about Deleting in a CRUD using php and MySQL. The way I was taught was to create Delete link and have delete.php run my delete() method on the id passed in the $_GET variable.
If I allowed a web crawler to see this page couldn't it delete the entire database if it crawled each delete link on each page?
Couldnt someone who somehow figured out the page that calls delete() manually load the url with whatever number they wanted to in the $_GET and delete records?
Is it better to never delete anything through the CRUD but to add a Deleted column to the table and make it appear as though it has been deleted to the user?
Am I just paranoid?
If I allowed a web crawler to see this page couldnt it delete the entire database if it crawled each delete link on each page?
Yes.
Couldnt someone who somehow figured out the page that calls delete() manually load the url with whatever number they wanted to in the $_GET and delete records?
Yes.
Is it better to never delete anything through the CRUD but to add a Deleted column to the table and make it appear as though it has been deleted to the user?
If you actually want to delete it, imo, deleting is better.
Am I just paranoid?
Not at all.
If you just had a delete.php script that had something like the following (ignoring CRUD atm and talking about MySQL):
$q = "DELETE FROM mytable WHERE row = $_GET['id']";
$r = mysql_query($q);
Then yes, your first two concerns are actually what would happen. A spider who stumbled upon these links would cause DB records to be deleted and a malicious user who discovered the links could similarly delete rows from the DB.
This is usually handled two ways:
Don't offer the delete.php link to people who shouldn't have it (people who are not logged in, people who are not logged in as admins, etc.) -- but note this is just a nicety and doesn't protect your DB from accidental / malicious deletion.
In the delete.php script, verify that the person running it is allowed to delete rows. Most of the time is done by checking session variables that were set upon login (e.g. if($_SESSION['can_delete'] != true){ die('Insufficient Privileges'); }).
Although it is helpful not to offer the delete.php link to people who shouldn't have it, you need to explicitly check for permissions in the delete.php script if you want it to be secure.
Read The Spider of Doom.
You can use a link to delete things, but the link should never be publicly exposed where it could be crawled, or even crawled by an internal indexing spider. Instead, you must check user authorization on every delete action to be sure the user making the deletion owns or is otherwise authorized to modify it.
So your delete() method must do more than just make the deletion (or the controller script which calls delete()). Always check for user authorization before performing any modification or deletion.
Whether you actually perform a deletion or simply mark rows as deleted depends on whether or not you need your application to be able to undelete them. Either way, check for authorization before acting.
Yes. This has happened to a fair bit of people, google crawls a "delete" page and remove their contents. I remember there being a really good blog post about it, but can't seem to find it.
Yes, this is entirely possible and very easy to do. That's why you need to build an authorization system. This means that before anything is performed, the system should check whether the user is allowed to do what he has requested, before performing the action.
Again, this dependings. If there is no use for the data anymore, then by all means delete it and get rid of it. But if you want to support some sort of "undeleting", then just mark the item as deleted.
No :)
One simple way would be to initiate a session like:
$somevar = 'abc123'
$_SESSION['secureDelete'][$id] = $somevar;
and then print this:
Delete
When you want to delete the file you check if that session exists with that $somevar value:
if (isset($_SESSION['secureDelete'][$id]) && $_SESSION['secureDelete'][$id] == $_GET['action']) {
deleteFunction($_GET['id']);
}
Try using POST method and hide the ID, it should be like this
<form action="" method="POST">
<input type="hidden" name="id" value="<?= $id; ?>"/>
<button type="submit">Delete</button>
</form>
I'm writing a web app in PHP (using Codeigniter). I'm just wanting some security advice here. I have a task controller with a delete method. The link to delete the task is then http://localhost/task/delete/12345
So I'm wondering how to handle the fact that people have the ability to modify the link, thus deleting another task (probably not their own).
So my options I guess are to always do a check that the user owns that particular task before deleting it, or some sort of ID hashing?
Does anyone have any suggestions?
So my options I guess are to always do a check that the user owns that particular task before deleting it
that is the usual, and best, approach, yes. Hashing the ID is too insecure for many use cases: The link containing the hash is stored in the browser's history, might get E-Mailed around, be present in REFERER headers for outgoing links....
Either check ownership, or use a full-blown Access Control List, depending on how complex the permissions are.
Yes, check whether the user is allowed to delete that task and respond with an
HTTP/1.1 403 Forbidden if he isn't. Also, make destructive actions like deleting records POST requests. Otherwise watch Google (or some other fetcher-type client) happily triggering all your delete actions.
It is not recommended to update/delete your data via an http get request. Use post instead.
So my options I guess are to always do
a check that the user owns that
particular task before deleting it
Yup, that's it. There's no point in hashing an id, as if some user guesses another hash id he might delete a task from another user (that would be security through obscurity, and it's not a great security method). So, yes, chech that the task belongs to the user before deleting it.
Also, it isn't clear from your question, but to delete something, the user should go to blablah.com/tasks/delete/1234, and then confirm to delete the task via POST (submitting a form by clicking a button probably).
You can use session to store the item's id to delete and then on the delete page you check that the id given is equal to the session stored id.
Hope it helps you
Sort of a methods/best practices question here that I am sure has been addressed, yet I can't find a solution based on the vague search terms I enter.
I know starting off the question with "Fast and easy" will probably draw out a few sighs, so my apologies.
Here is the deal.
I have a logged in area where an ADMIN can do a whole host of POST operations to input data relating to their profile. The way I have data structured is pretty distinct and well segmented in most tables as it relates to the ID of the admin.
Now, I have a table where I dump one type of data into and differentiate this data by assigning the ADMIN's unique ID to each record. In other words, all ADMINs have this one type of data writing to this table. I just differentiate by the ADMIN ID with each record.
I was planning on letting the ADMIN remove these records by clicking on a link with a query string - obviously using GET. Obviously, the query structure is in the link so any logged in admin could then exploit the URL and delete a competitor's records.
Is the only way to safely do this through POST or should I pass through the session info that includes password and validate it against the ADMIN ID that is requesting the delete?
This is obviously much more work for me.
As they said in the auto repair biz I used to work in... there are 3 ways to do a job: Fast, Good, and Cheap. You can only have two at a time. Fast and cheap will not be good. Good and cheap will not have fast turnaround. Fast and good will NOT be cheap. haha
I guess that applies here... can never have Fast, Easy and Secure all at once ;)
Thanks in advance...
As a general rule, any operation that alters state (whether its session state, or database state) should use POST. That means the only 'safe' SQL operation you can perform with GET is SELECT. Even if you're only using a back-end admin thing, you shouldn't use get. Imagine re-opening your browser and finding that the last time you closed firefox was on your 'DELETE EVERYTHING' GET->delete page resulting in everything being deleted again.
One of the main reasons for this is preventing cross-site request forgeries. For example, if you had a page that took a GET variable such as http://example.com/account?action=logout, an attacker could post an image on your site like this:
<img src="http://example.com/account?action=logout" />
and anyone who opened a page containing that image tag would be immediately logged out, even if they were an admin. It would be very annoying to then search through your raw database for that data and remove it.
Although POST operations are 'nearly' as easy to forge, as a general rule with any web security issue, the trade-off is speed/simplicity vs. security, so you're going to have to choose one or the other.
You should have some kind of session set up.
Using POST over GET gets you nothing tangible as far as security is concerned. POSTs can be forged just like GETs.
So assuming once your admin logs in, you've got some kind of identifier in the session, you just leverage that.
Consider something roughly similar to this:
<?PHP
session_start();
if (empty ($_SESSION['admin_id'])) die("Log in, son.");
if (empty($_GET['record_id'])) die("You've got to tell me which record to delete!");
if (! is_numeric($_GET['record_id'])) die("Invalid record ID");
//just to be totally safe, even though we just enforced that it's numeric.
$record_id = mysql_real_escape_string($_GET['record_id']));
$sql = "DELETE FROM table WHERE record_id='{$record_id}' AND admin_id = {$_SESSION['admin_id']}";
mysql_query($sql);
echo "Record was deleted (assuming it was yours in the first place)";
?>
In that example, we're avoiding the "delete someone else's records" problem by leveraging the WHERE clause in the DELETE query. Of course, to be more user friendly, you'd want to first fetch the record, then compare the admin_id on the record to the admin_id in $_SESSION, and if they don't match, complain, log something, throw an error, etc.
HTH
You're saying: "What if Admin 123 access the URL of Admin 321 directly, thus deleting his stuff?" no?
If that's so then, every admin that is logged in should have at least one session with some unique identifier to that admin. If not, he shouldn't be able to be in the admin section in the first place. Can't you just compare the identifier in the URL with the identifier in the session?
If any user, not just an admin, access those 'delete me' URLs he should be identified by a session as the original 'owner' (admin) of that data.