I have a php page which does an insert and then does a redirect to a "success" page which displays the order details. I am redirecting by using a variable, but I am thinking this is not so secure:
header("location:success.php?id=$orderid");
This generates a url like this: success.php?id=22
I am then using the URL to obtain the order ID number and doing a query based off of that to present the order details:
$getName = explode('?id=', $_SERVER['REQUEST_URI']);
$id = $getName[1];
$query = <<<SQL
SELECT * FROM `order` WHERE `orderid` = ?;
SQL;
$request = $mysqli->prepare($query);
$request->bind_param("i", $id);
$request->execute();
The obvious issue is that anyone could simply change the URL id number to get the details of a different order number. I'm not too terribly concerned as this is strictly an internal site, but I'd still like to fix this behavior. Is there a better, more secure way to do this?
Here is an easier solution: Store orderid in the session like this $_SESSION['orderid'] = $orderid.
And on redirect you'll get it's value as below:
$getName = explode('?id=', $_SERVER['REQUEST_URI']);
$id = $_SESSION['orderid'];
$query = <<<SQL
SELECT * FROM `order` WHERE `orderid` = ?;
SQL;
$request = $mysqli->prepare($query);
$request->bind_param("i", $id);
$request->execute();
In general, the problem of allowing a customer to view a past order while not allowing others to view this order has two steps.
Set authorization policy: Determine which users are allowed to view which order numbers. This ought to include the ability to come back in a later session and view the status of an order.
Back it with an authentication method: Store and retrieve enough information to identify the user.
Authorization policies to consider include the following:
The user who just placed an order is allowed to view it in the same session. This is the policy you describe in your question.
Placing an order creates a unique ID. Anyone with this ID is allowed to view it.
Someone who creates a user account, provides an e-mail address and verifies ability to receive mail at that address is allowed to list orders associated with that billing e-mail and view them.
Someone who provides the correct order ID and shipping postal code (or other PII needed for order processing) is allowed to view that order, but not list any, and this is rate limited in some manner for each IP address. (Users behind a big NAT have the other three choices.)
To implement policy 1 (same session), you can store the order ID in the session, whether in PHP's $_SESSION or in a separate session properties table, as soon as the order is marked as paid. Then when the user tries to view an order, you can allow it if the order ID matches the most recent for this session.
To implement policy 2 (unique ID), you can generate and store a suitably long random identifier, such as a version 4 UUID, and store it with the order. Warn the user: "Be careful: anyone who sees this order ID can view and change your order."
To implement policies 1 and 2 in one stroke, you can store the session ID with each order, display the session ID on the success page, and then let the user search for orders associated with a particular session.
To implement policy 3 (control of billing e-mail), you can add a user table including user ID, password hash, e-mail address, and (nullable) e-mail confirmation date. This would also give you a chance to add user-created product lists, which lets you add features such as an alert when a product comes back in stock.
To implement policy 4 (order ID and shipping info), you can make a table logging the IP address, date, and more information about each request, apply a heuristic to determine whether a scripted brute force attack is in progress, and then compare the shipping postal code provided by the user to that in the order.
Keep these policies and mechanisms in mind as you continue to develop your store application, as they are likely to increase its usability.
On your system I assumed your orders are related to you users.
For example order: 1234 has relation with user: 5
Check this relation on your success.php
If your scheme has not user-order relation I strongly recommend it. But if orders doing by anon users, hash order_id
basically like
select * from order where order_id = 1234 and userid =5
If this query returns a row, that user really has this order. Else something is wrong.
if($returnedRow == 1){
//Do something here
}else{
//access denied. You are not owner of this order.
}
As I said, count returned row, this is simple way. Also if you hash your order_id like dfljfk45498ds545 it can be less predictable. You can use uniqid() for this purpose.
use cookie or session to send "$orderid"
*session is better
Related
I made a login function with bruteforce protection, different bot protection functions like honeypot.., input filtering, Argon2 encrypted password.
But in the end to identify the user i save the id inside a session after a successful login.
With this id until now i checked if the column "admin" has the value 1 or 0.
If this value is 1 the user can do everything.
How i could improve the security ?
How else i could check if a user is an admin ?
You should do as I will direct you
As long as you have user id in act so it's half of the way
I will assume that you have function.php which have all the functions you use in website
Also you have login function that check user account details and give access
You now need to improve that to restrict user access
First:
Create table in MySQL call it group this table will have two records or as you like so. Admin will have id 1 and members id 2 then you can make much modifications like make another column in that table called upload and set admin to yes while members to no
Second in your login function use the following
$res = mysql_query("SELECT * FROM users INNER JOIN group ON users.class=group.group_id WHERE users.enabled='yes' AND users.status = 'confirmed'") or die(mysql_error());
$row = mysql_fetch_array($res);
Users.class is the group id and users is the table users
Now you can check group credits as example if on upload page you do the following
if($row["can_upload"]=="no")
echo("admin only can upload ");
You can check credentials on group as you need also you can make much classes like admin , members, uploders, reviewers,authors and give every class special permissions as website content is viewed
On admin.php you can use
if($row["admin"]=="no")
ech("admin only can view this page ");
In both examples above Admin and can_upload is colum in group table and it's changed by user class .
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/)
I'm looking for guidance on the database structure for a multi-regional website.
I am setting up a website, similar to craigslist.com, that will allow users to post adds in their city. I am using a MySQL database.
I want to have regions as subfolders linked to subdomains e.g. ca.mysite.com goes to mysite.com/ca.
My question is how do I manage the database(s) when I want to add another city to my website.
If I use a different database for each city, then users wont be able to login to other cities as their login details are stored in the city they registered at in the users table of it's db.
This may not be an issue though, as the content is city specific, like craigslist.
But, should they wish to contact users in other cities, they wont be able to.
Also, there would be duplicate username's and email addresses overall, as users could register in all of the cities using the same email and username.
If I create a central database with for instance, a users table, and a messages table, and then a separate db for each city with all that cities 'posts' then when displaying basic information I will need to consult the city specific db plus the central db that stores user info.
Alternatively I could store everything on one database, and store the logged-in user's location in a cookie, pass that to a session variable, and use that location as part of the database query when displaying search results etc.
However, would this not add an unnecessary overhead to each query?
e.g. every search would have to have ' AND location = $user_location ' added in.
I really have no idea what would be the best method here.
Thanks in advance for any help on this.
It seems that you still do not know clearly the system you want to build. Before starting designing a database for this system, please make sure that you have the full description of the system requirements. Some sample questions that help you clarify are:
What features does your website offer?
What are all the actions that a user using your system can do? With each action, is there any restrictions?
Some other questions relating to the system performance:
- How many users do you expect to use your system?
- How fast and correct each action should be served? Which actions are used frequently?
It makes no sense to design a system without a careful understanding about the system requirements.
So here is the sample database design and how it can support your requirements.
Cities(cityid, cname)
Users(userid, fname, lname, cityid, currcityid)
Messages(mid, senderid, receiverid, content)
Adverts(aid, title, content, userid, cityid)
When a user switches the city, update the currcityid field in its row in the Users table.
When a user posts an ad on a city, insert a new row to the Adverts table. The userid and cityid of the new row are the ids of the corresponding user and city.
When a user sends a message to another user in the system, add a row to the Messages table. The senderid and the receiverid of the new row are the ids of the corresponding users.
Query all ads in a city: SELECT * FROM Adverts WHERE cityid = $cityid
Query all ads of a user: SELECT * FROM Adverts WHERE userid = $userid
Query all ads of a user in a specific city: SELECT * FROM Adverts WHERE cityid = $cityid AND userid = $userid
Hope this helps.
I have a site where users can place an order. The order goes through various stages before it is ready for delivery. I want to be able to log anytime anything happens relating to an order.
Here's an example of what I would like for it to look:
(2/13/12 4:41pm): Order initiated by Customer (2/13/12 4:41pm): Order
sent to Manager for approval (2/13/12 4:43pm): Order approved by
Manager (2/14/12 6:03pm): The order was edited by the user: City: 'Los
Angeles' to 'San Diego' (2/14/12 6:09pm): The order was edited by the
admin: Email: 'gearge#gmail.com' to 'george#gmail.com' (2/15/12
8:41pm): Order ready for Delivery
What is the best approach for me to store this type of order history? I have created a table in the DB called history where I would like to store the history as it pertains to each order. My columns are history_id(primary), order_id(foreign), date added(timestamp), and history_message(varchar).
Ideally I would like to create numbered codes for each step in the ordering process, such as an order approval, order edit, or order delivery and then just assign the number for that row instead of the actual history message, which is more characters. I feel that this way I won't overstuff my DB. The problem with this approach is that I would like to keep a log of the actual data that was changed(as you can see in the edits by the user and admin in the example) and I'm not sure how I can accomplish that without saving the complete history message for each row.
What is the best way to store order history in a database for my situation?
You can use trigger to update a child table keeping track of each order status.
Basically using insert trigger or do manual insert after update in order status by insert query you can copy required data from orders table to a new table we can call trackorders. tracking table will keep track of all changes and record with current status will be in main table.
I have implemented search for my customers master successfully, and it searches the entire customer master.
I have agents logged in who are doing the searching. Customer accounts are associated with agents. I need to restrict the search to customers associated to the agent (who is logged in).
How do I do this?
You will need to scope your data and your sql statements to the current user.
For instance, let's say you have a table sales and a table users. Functionally, each sale belongs to a user. As such, the sales table should have a foreign key on it such as user_id that identifies, for each sale row, the row in the users table to which that sale belongs.
Then, when you search through sales, you should always add "where user_id = ?" as the first filter of your sql statement, before the other dynamic filters, replacing ? with the id of the current logged in user.
In this manner, all the filter criteria when searching the sales table will first be scoped to the current logged in user. If the filter criteria would otherwise pick up someone else's sales rows, it will no longer do that due to the user_id filter.
If you have sales that pertain to all agents in addition to those that are agent-specific, they would presumably have some marker, either an agent_id of 0 or perhaps NULL, or some other field that identifies them as searchable by all. This can easily be worked into that first WHERE fragment in the SQL statement with appropriate parentheses to keep it together:
WHERE (agent_id = ? or agent_id IS NULL) AND other dynamic filter etc
WHERE (agent_id = ? or agent_id = 0) AND other dynamic filter etc
WHERE (agent_id = ? or all_agents_flag = 1) AND other dynamic filter etc
Seems like you need to LEFT JOIN. Why don't you LEFT JOIN the tables that are in question. For ex: orders, sales_agents, customers...
Might just work.