Protecting against record ID manipulation - php

How do you prevent a malicious user from changing URL or form data, specifically a record ID. For example:
http://example.com/deleteproduct.php?id=34
The user could change the ID value from 34 to say 69 and in doing so delete a record belonging to another customer. I guess the obvious protection is to validate the ID before performing the delete to make sure the user has access to that record but is there perhaps another approach that is consider better practice? The downside of validating the ID requires more database queries which would be great to avoid.

I guess the obvious protection is to validate the ID before performing the delete to make sure the user has access to that record.
This is the only way to ensure that your user has access to delete these rows.
The downside of validating the ID requires more database queries which would be great to avoid.
Not necessarily. You can simply check when you're deleting to only delete rows that belong to your user.
For example, assuming your table structure looks similar to:
users
-----
id | username
1 | Dave
2 | John
products
-----
id | name | user_owner
1 | Milk | 1
2 | Cake | 2
So if Dave visited deleteproduct.php?id=2, the following query would execute:
DELETE FROM products WHERE id = 2 AND user_owner = 1;
It wouldn't delete anything, and $mysqli->affected_rows would return zero.
When affected rows is zero it means that the product ID was invalid or the product didn't belong to the user, either way: you would display a message telling the user that the product id is invalid.

Do they have authorization to delete items? If so, does it matter?
If it's per-record authorization... just check if they have authorization for the requested id. Long answer short: check if they're authorized and never trust user input.

What you said is very dangerous.
Security
In the first place you need to protect data and make it only available for the selected users. Please create or finetune your access list with roles and permissions.
Show only links/buttons for elements that may be accessed by the (logged in) user
Check before access the elements by the (logged in) user the permissions (and role). Not only the action, but also the data
Authentication (user logged in or not?), authorization (has the user the right permissions to access the (data) element?)
In the second place, it's not a right approach to use internal id's public.
It's always good to hide your internal id's, because it makes your application more safe.
Data Access
You have to access your data. The most simple idea is checking it in your query, like so:
DELETE FROM table WHERE id = 34 AND user_id = 1 // Delete only if id = 34 and user is 1
Do you understand the idea?
Internal Id's
You can encode your id's by using existing algorithms. Decoding is only possible with your secret key. There are many solutions and packages.
Hashids is a small open-source library that generates short, unique, non-sequential ids from numbers. (http://hashids.org/php/)

Related

Validating users upon login and giving them limited rights to database

I'm new to PHP and MYSQL, trying to create a website which users can use to input data into a database. An example of what i'm trying to do would be a database for various banks and the various services they provide.For example, a user from Citibank creates an account on my website, he will enter his LoginID,Password,Email & the name of his bank(which would be Citibank in this case).
Upon successfully creating an account and logging in, he would be the "Admin" account for Citibank with the rights to Create,Delete,Insert & View all data from Citibank ONLY. He would also be able to further create & delete Outlets, and create/delete a SubUser account for that outlet.The SubUser account would have all the rights the Admin account would have minus the right to create further SubUsers, BUT restricted to only the Outlet it is in charge of.Both Admin and Sub accounts would be logging in through the website.
I've listed down the rights which i think the accounts would need:
Rights to database
SELECT,INSERT,UPDATE,DELETE,(JOIN?)
I am currently thinking of implementing the following table for the Admin account:
Admin
+----------+-----------+------------+------------+
| BankID | BankName | UserName | Password |
+----------+-----------+------------+------------+
| 1 | Citibank | CitiAdmin | PassCiti |
| 2 | StanChart | StanAdmin | PassStan |
| 3 | HSBC | HSBCAdmin | PassHSBC |
+----------+-----------+------------+------------+
Where the BankID would be of type SERIAL, while the BankName,UserName and Password would be entered by the user upon creation of his account.The reason why i do not split the above table into 2 tables with one containing the BankID and BankName and the other containing Username & Password would be for ease of use as i feel that splitting it up would be needless, and be over-normalising it.
While the following table would be for the Subuser accounts:
SubUsers
+------+------------+--------------+-------------+
| ID | OutletID | Name | Password |
+------+------------+--------------+-------------+
| 1 | 1 | CitiSub1 | PassSub1 |
| 2 | 1 | CitiSub2 | PassSub2 |
| 3 | 2 | StanSub1 | PassSub1 |
| 4 | 2 | StanSub2 | PassSub2 |
| 5 | 3 | HSBCSub1 | PassSub1 |
| 6 | 4 | HSBCSub2 | PassSub2 |
+------+------------+--------------+-------------+
By doing this, upon user login, i would get the userentry from $_POST[User] and $POST[Pass] and match if against the data drawn from the query
$query="SELECT Username AND Password FROM Admin AND SubUsers";
and if there is a match, the user will be logged in.By doing this i am able to achieve a first level of verification where only registered users are able to access the database.
However how would i restrict access to both the Admin account, AND the SubUser account.The Admin account would only be able to access data pertaining to his Bank, and the SubUser account would only be able to access data pertaining to his Outlet.
I've considered using PHP sessions to perhaps record data about the user when logging in by changing the login query from
$query="SELECT Username AND Password FROM Admin AND SubUsers";
to a query that first selects Username and Password from Admin, and runs the $_POST[User] and $_POST[Pass] through it, and if there isnt a match it would draw Username and Password from SubUser and repeat the process, and would log a result into the session depending if the match happened in the Admin table or SubUser table.
However,doing this would only change the webpages available to the user upon login and not their actual access to the database itself.The closest solution i can think of using this method would be to create a brand new set of webpages for the user depending on whether the user is an Admin or SubUser, which i would rather NOT do as i am still new to programming, and increasing the number of webpages would only increase the number of bugs that will ineveitably show up.
Are there any other methods to restrict user access to the database, and or other solutions to optimise what i'm trying to do?
I've looked at How to configure phpMyAdmin for multiple users - each with access to their database only but it's a little too technical for me and seems to be dealing with user access to databases instead of tables.
Any advise/help/guidance will be MUCH appreciated.
What an interesting and thorough question. It is rather of the type that requires a book to answer thoroughly though. I admire your ambition.
First design it properly.
Ask yourself what actions users might need to do and give them a name. Once you store the privelege names in a table, you can assign them to roles or users as required. You authenticate the ability to do each thing at PHP level, either by checking before each action that the appropriate privelege is applied or by writing each action as a function that includes authentication of priveleges.
Put the bank id and branch id as Foreign Keys in each table. That way you simply include the bankid and branchid as 'AND' additions to your WHERE clause. This wa you need only one database but you control who gets to see what using intelligently written SQL.
If you need the users to be able to run SQL on their data, ensure that all queries are run through a function that adds the requisite AND (bankid='%s' AND branchid='%s') clause. This effectively separates the data. You can add a check of your returned data if you need to and also consider using encryption (different key for each bank) though that is going a bit far.
This pretty much is what is meant by application-layer control. The PHP application selects what data you have access to based on stored priveleges. I cannot re-inforce how important it is to plan your priveleges, given them meaningful names and verbose descriptions. It seems a lot of work when you start but it makes the difference. it certainly beats having to create a new database for each user. Don't worry about filling up your SERIAL ids - a BIGINT can handle a million transactions per second for over 200,000 years.
Once designed, authentication is the next hurdle. I reckon you should do this before you write anything fancy as it's really quite hard to get right.
What I would do is:
Collect bank,branch and username (allow these to autocomplete in your HTML) and then password.
Store the password as an SHA1 or MD5 hash.
Once authenticated, you pop the usernumber, bank and branch numbers into your $_SESSION They can then easily be retrieved for SQL later.
For added security, though increased complexity, you can also pick these numbers out of the database as required. Some recommend storing them in a separate session table.
There is so much more to say about how to design this sort of project and much of it can be found elsewhere on this site so I will not prattle on further. Please feel free to ask if anything is unclear.
I hope this helps.
EDIT:
Handling the priveleges.
There is no simple way to handle priveleges. I use a single header file for all my pages that automatically extracts privelege information:
a. Identify the user, usually picking the usernumber from $_SESSION.
b. Identify the user's priveleges from the DB table users_priveleges.
c. Create an array containing the privelege names.
d. For Each through the array to compare whenever a privelege-required operation is required.
This method needs a lot of tables and is perhaps a bit advanced for your needs but if you have the following tables (skeleton details provided here only) it is pretty much infinitely expandable:
roles (role_id,rolename,role_detailed_description)
priveleges (privelege_id,privelegename,privelege_detailed_description)
users (user_id,user_details)
users_roles (user_id,role_id) (optional but a good idea)
users_priveleges (user_id,privelege_id) - priveleges granted to each user
roles_priveleges (role_id,privelege_id) - the priveleges each role has.
What you do is enter a line in the roles_priveleges table linking a role to a privelege. Repeat for all priveleges required by the role. Could be a lot. Not a problem.
When a user is added, you grant them a role. I then read the roles_priveleges table and present the super-user with a list of possible roles as checkboxes, ticked if the privelege would usually be granted, not if otherwise. The super-user deselects or selects from the list as required then saves the list.
On saving the list, I mark all entries for that user in the users_priveleges table as inactive and insert a new line for each privelege. This allows you to track the changes and, importantly, the date the priveleges were reviewed, even if they were not changed. It does not end up using much data as each line in users_priveleges consists of three Bigints,a bool and 2 dates.
If you never want to grant one user a privelege that their role would not normally posess then you can simply use roles_priveleges and users_roles. This is minimally less data-hungry but is notably less flexible.
I will concede the method I have described is a little inelegant but it provides very good role based and user based privelege management whilst keeping the DB in the 4th Normal Form or higher. IMHO it is worth going the extra mile because your application will one day be bigger and it is far easier to add this stuff now rather than later.
Also, from a beginner's point of view, it is very easy to create dummy data and ensure your SQL joins are working before you embark on something a bit harder.

PHP displaying links based on user permissions?

I'm trying to show or not show links based on a users access level. The links will be different depending on the section of the site the user might be in. The links also may not all be in one menu. They will more than likely be in various places on the page.
Currently I have a database table that contains Users, Groups and Sections. The main menu is built from the Sections database table. I'm thinking I should create an Actions table and add a link that I'd like to show for each section in the action menu. So, my tables so far are like.
Users
user_id
Groups
group_id
group_title
Sections
section_id
section_title
Table I'm thinking of adding.
Actions
action_id
action_title
action_group_id
action_section_id
The part I'm not sure on is should I add the same link multiple times to the Actions table for each group that is allowed access. Or, just add it once and do a if group id is greater than, then show link.
Example for entering the same link multiple times.
action_id action_title action_group_id action_section_id
1 View all 1 1
2 View all 2 1
3 View all 3 1
I was hoping to not flood the page with a bunch of if/then statements. Plus, this doesn't seem like the best way to handle because it requires human interpretation as to what the access levels stand for.
Any help on this is appreciated. I could be going in the complete wrong direction here?
Create a many to many relationship with an additional table where you insert an entry for each permission the group has access to. Am I correct in assuming section is what you're creating permission to?
Table: Group_Section (Or whatever you'd like to name it)
Group_id | Section_ID
---------+-----------
1 | 1
1 | 2
1 | 3
2 | 1
2 | 3
|
You can see that the Group with ID = 1 can access sections 1,2,3 while Group with ID = 2 can access only 1,3. You can then add whatever permissions to the table you want and manage them through the use of foreign keys.
Does that make sense?
Here is a good article but the things are discussed in general http://en.wikipedia.org/wiki/Access_controlenter link description here
In your case, use what TheCapn wrote and I'll just add, that its 'best to start session for every user and just check his access level when he's trying to reach a restricted part.
Personnaly, to do this kind of thing, i set a user level in the user table and a section level in the section table.
Then you simply have to filter the section according to your user level.
You can do this by adding a statemtn to you sql like
AND section_level >= "user_level";
or then again, get all the section and filter tham with php.
foreach($section as $s){
if ($s->level >= user_level) echo $s->title
}
Of course, you'll need to adjust the <. = and > according to the hierachy of your system.
I personnaly use a lowering hierachy, meaning, the lower the level you are the more right you have. This way you can make a 'banned' user by setting his level to 99 or something.
THis would be only for your menus, make sure you control the user_level on each page as well so if someone get to the page directly it get kicked..
Hope it points your in the right direction. ;)

Mysql foreign key constraint, update on delete (assign all linked to another)

I might be approaching the problem from the wrong point of view so please correct me if my technique is wrong.
I'm creating guest users on a website with php. When someone visits the website, a guest user is automatically created. But if that person logs into a "normal" account (non guest) then whatever that person did in the guest account is transferred to the new account.
Example, someone visits a website without authenticating, add products from the shop in his cart and then logs in. I will need to transfer the cart items from the old user (guest) to the new user.
But it would be too time consuming (and possibly too resource hungry) to find every item in every table that is linked to the guest user account and then change them all. Right?
But I've got constraints! Yay! If I update the guest user id, all the constraints change. Fabulous! But what if I need to assign them to another user id? How would I do that?
Any help is greatly appreciated.
I am using session array to manage it, previously i was doing through database table but it was too tedious.
When user add any product to cart if he is logged in then insert in order tables and if not then add in session array. Now when guest login into system then check session array and if it is not null then add all entries in user order tables.
You can't do exactly what you're suggesting because the user row (for the logged in user) presumably already exists. The only option would be to go through each table and update the UserID there, like you suggest. The feasability of this depends on what they can do as a guest user. If it's only one or two tables, just do it this way.
But, every problem can always be solved by another layer of abstraction, right? Put another single table (I'll call it "pseudousers") in between the real users table and everything else. Link everything else to this new table, and use the "psuedousers" to link up to the users table. When the guest user logs in, just change the UserID in the pseudousers table and you're done.
What I mean is:
+---------+ +-----------+ +-------+
| Users |----|Pseudousers|----| Carts |
+---------+ +-----------+ +-------+
|
| +-------+
|------------| ... |
+-------+
When you create a guest user, you create a new row in Users and Pseudousers. When they log in, update the UserID in Pseudousers to point to that user, and then you can safely delete the guest user row in Users (it's no longer in use).
Hopefully someone can come up with a better name than "Pseudousers" though.

Best practices for website permissions system

Current project I am working on is a web application, that has to be delivered to multiple customers on their own servers. The website needs a permission control system, that will manage areas and features users can or can not use.
As for know, I'm designing a database-driven permissions system, with permissions stored in database. Every user has a role. The role defines the list permissions available. The schema looks like this:
users table
1. user_id
2. name
3. role_id
roles table
1. role_id
2. name
permissions table
1. permission_id
2. name
roles_permissions table:
1. role_id
2. permission_id
In the code I would fetch logged users role and permissions, and check if the user is able to perform action or see area like so:
if($user->hasPermission('Edit HR')) {
// let user see the editing HR section
}
The hasPermission would check if user has a permission with a name 'Edit HR' and will return the needed result. The problem I see is the database table has to have the permission record with a name being exactly 'Edit HR', not 'Edit_hr' or 'HR Editing'. So I have to make sure the data for the permissions system is the same for every database the applications are using. Which kind of makes me think this is a flawed design and needs to be re-designed. Creating new sections and features would also require to update all the databases, which also makes me a sad panda.
So, basically, the question is: what is the best way to design the database driven permission system and keep the database integrity on multiple databases?
The scheme you've come up with looks fine. The only thing I would add to that is on the permissions table I would add a field called tag or similar.
The permission tag would be something like EDIT_HR, and you would use this as the reference in your code instead of its name. Use the name just for display purposes for example HR Editing. This way the name can vary as required, and it won't affect your code.
The solution I'm using is to have a global $current_user object that reads the permissions table on creation and stores all permission actions that are valid for it. This array is then searched whenever you need to check an action. It saves on DB queries, although if there are security implications for storing this kind of data in a global object, I haven't found it.
There's only 1 db table required (sample):
user_id | user_role | user_action
---------------------------------
0 | 10 | view_dashboard
0 | 1 | view_users
User role corresponds to the minimum user type (Admin, editor, visitor, etc.) so all actions with a user_role >= $current_user role are available. The user_id column allows you to override certain levels for a specific user.
With this kind of setup, it's also easy to have a page that lists all permissions and allows a user to modify the value with a simple dropdown (but make sure not every user can do that).

Setting up a MySQL table to track user link clicks

This is a followup to a question I posted a few days ago.
basically, I have a site with six links. In order to access the site, users must log in using LDAP authentication. When they do this, I grab some of their account credentials (username, firstname, lastname), and store it in a PHP $_SESSION variable.
That works; the user can log in, and the session data is being stored successfully.
Now, I want to set up a way to track which links have been clicked by what users. Basically just store a time stamp in the database of when they clicked the link. I want to be able to see who has (or has not) clicked each link, and when.
Can I do this in a single table / would that be a bad idea? I was thinking setting up the table like this:
TABLE (each bullet indicative of a column)
auto-incrementing ID
user account name: abc1234
user account first name: John
link 1: Last Accessed 5/2/2012 at 4:15PM
link 2: NULL
link 3: NULL
link 4: Last Accessed 5/1/2012 at 2:20PM
link 5: NULL
link 6: NULL
basically the above would say that "John" had only clicked the first and 4th links. The rest are null because he has never accessed them. If he were to click #1 again, it would overwrite with the more recent date/time.
Can I do this in a single table? or will that create complications? I feel like the thing I will have the hardest time with is checking to see if the user is already in the database before adding the info (ie so that if John logs in a second time, a whole new row isn't created for him)
Thanks for any help!
That would be a bad idea. What if you wanted to have a seventh link? What if the user format would change?
This solution requires 3 tables:
Users - contains user data (And a user ID).
Links - contains link data (And a link ID).
Clicks - many-to-many relationship between users and links.
That third table would look like this:
user_id | link_id | timestamp
-----------------------------
1 | 2 | ...
2 | 2 | ...
1 | 3 | ...
............
why not just have
increment_ID
Account_ID
Link_URL
Timestamp
Then just insert a new record for each click. You also don't need to manage links since you'll store the entire URL path

Categories