Including obscured IDs in the URL - php

I need to include the user ID in the URL like this:
http://www.example.com/user.php?id=123456
However, there is a problem. If a user manually changes the ID in the URL to 123455 for example, that could potentially lead to an erroneous update of the database.
For this reason, I need to somehow make the ID in the URL unreadable to the user so they can't just subtract 1 from the ID and be able to alter another user's data.
Another requirement is that the ID in the URL must be usable, meaning that whatever we do to it, PHP must have a way of figuring out the database row corresponding to that particular ID.
There are 2 possible solutions I can think of. I would be happy to hear your opinion on which one is better. If there's an even better solution that I haven't thought of, please let me know.
Including an encrypted version of the ID in the URL - that should make it extremely difficult for a user to just change the ID in the URL and guess another user's encrypted ID. It's also easy for PHP to decrypt the ID when needed and use it to request the user's data from the database.
Adding a new column called "hash" in the "users" table in the database. As you may have guessed, every user will have a unique random hash or UUID stored in the database which will be included in the URL. That makes guessing another user's hash very unlikely. PHP can easily retrieve the user's data by using the hash in the database query.

If a user manually changes the ID in the URL to 123455 for example, that could potentially lead to an erroneous update of the database.
The way to solve this problem is to have sanity checks on the server so the user is not allowed to erroneously update the database. You either want some sort of permission checking ("this user is not allowed to update this record"), or other consistency checks that ensure no updates can be made erroneously ("the user is generally allowed to update this record, but right now it would cause a conflict with something else, so we won't").
You will have to include some id in the URL, and a user will always be able to change that id. At best you can make valid ids harder to guess by using something other than consecutive numbering, but that doesn't solve the underlying problem that your server has no sanity checks. Don't fault the user for generating errors, it's your code that's allowing it.

You should use sessions for this, not GET[] parameters, sessions are the tool for this Job. You can try the solutions you think of but from my point of view using sessions will be a lot better and simpler to use and implement.
But if you need to do something like the classic "recover my acount" so you don't have a way to log in you user, them you may use a hash in an URL and send it by email to "ensure" your user is the one who get's the URL.

Related

Reveal the User ID over the url? E.g.: www.domain.com/user/1

What is the best practice?
I have a website, that has a user profile page and you can get to it with domain.com/user/1. Every number reveals the User_ID from my database. I'm curious is this a bad idea? I'm not into SQL injecting, but maybe one can use the information against me. I'm using Laravel and it has some basic protecting against injecting.
What do you think? Should I use random numbers for the user profile or it doesn't matter at all?
Note : Writing in context to laravel
Yea, it depend entirely on the purpose you want to use it for.
Lets say, for example in simple product listing an url that looks like
http://www.demosite.com/seller/1234/products/1234
would disclose vendor id and product id associated. so, even if someone try to do something like
http://www.demosite.com/seller/1235/products/1232 would fail if the vendor or product does not exits which is totally fine.
Also, as you are using laravel likely the selected routes will be only visible to user authenticated so, first level of security is achieved and you only have worry about internal users for which if you are still worried
use Hashing or encryption (again it depends entirely on the sensitivity of data.)
You can look here.. for Laravel Hashing or Encryption
if you have any queries comment back..
If depends!
If there is anything at all sensitive that can be accessed with just the id then it is a problem.
However if there is nothing sensitive – not even when preempting the id of an account before it is created1 – then there is no issue.
Consider for example https://stackoverflow.com/users/7009480/philipp-mochine which contains your user id here on Stackoverflow.
1 A multi-stage registration would be a problem with this: attacker jumps ahead to the last page and sets authentication details while the real person is still filling in their profile (fix: create the account and password then the user can fill out everything later).

Random ID Number when user created

I want to use a users unique id to save a cookie - so that I know which user is logged in, and then I can change their content to suit.
I am currently just using the usual auto id when a new record is created, but I have heard that for creating user accounts (specifically when you're going to use that ID to change content) that you shouldn't have them 1 after another; e.g. not 378, 379,380 and so on but more like this 138462193, 109346286, 982638192 so it's kind of like a random unique identifier.
How would i achieve this?
Is this a best practice?
You protect your data against attacks by using ACL, to limit which user has access to to what (and with what data). Foreign key relations to establish ownership between user and data, session ID regeneration at login, CRSF tokens to prevent attacks via other sites, and so forth.
Not to mention logging, to be able to find out what went wrong when things do go wrong.
Only in very special cases do you ever need to worry about the ID of users being sequential. Most of the time this ID will be available to other users, via the web site itself, anyway. As a part of normal operations.
Thus adding a random element to the user ID won't bring anything but a false sense of security. Even if you keep the internal ID different from the "external" user-facing ID, as long as you're using the external ID to identify and change content it's basically the same as the internal ID. Only valid reason for using a dual ID system, in most cases, is for human readability. If you're uncertain about whether your use case is one of the exceptions, it's not.
PS: I see in your comment that you say that the passwords are encrypted. Hopefully you mean "salted and hashed", more specifically by using password_hash () and it's associated functions.

Is it bad to have file name as post variable?

I don't know if there is a way for someone to potentially abuse this. What is a workaround? I do not want someone to be able to abuse my server by downloading content this way.
There is an option to re-email a shipping label. I basically have it setup so there is no database work besides the preparation of the page. The only other method I can think of is to have the post variable the ID of the row and then pull the file name from there.
So, is it unsafe to have a filename as a post variable (that could potentially be tampered with)?
It's only unsafe if you're going to do something like readfile() or include() on it.
Using the row ID would be better, but even with this you still need to consider if the user should be allowed to access the file (to avoid random id=1 id=2 id=3 testing).
You should never trust incoming data. POST requests can be abused as well as GET requests, so in the worst case your website could be a mean of XSS attacks, or could be used as a spamming node.
The thing to do is sending, apart from filename or entry ID, some hash, and then checking whether these two match, i.e. is the entry associated with this hash. Therefore simply trying different IDs will not work, since abuser would have to guess proper hash as well. A word of advice: hash not only id, or filename, but time of sending e-mail and some salt as well.

SECURITY: Secure id in a url

I use the following url when I edit a post from the user :
../post/edit/3 //If the id of the post is 3 for example
To avoid that the user modifies the url intentionally, for example /post/edit/5, I use the following logic to make sure the user doesn't edit the post when he doesn't have permission:
if (//user is allowed to edit post){
//edit post
}
else {
throw new AccessDeniedException('You do not have the permission to edit this post');
}
Is this the general approach that you use when editing a post? Is there a way to do something cleaner so that the user cannot play with the id of the post in the url?
EDIT
The more I think about it, the more I realize that I have never seen an id in a url like this in a website that is concerned with security. So, I agree we can still use the id and check if the user can show/see this id, but still the user can already do too much.
Wouldn't it be better to hash the id, allowing us to generate a new encrypted ID using any available algorithm:
<?php
echo hash('md5', 'id_to_edit');
?>
What is the standard approach to secure an id in a url? In general, is it a good idea to display info like the id in a url?
Special situations may call for special measures, but in a typical situation, all that is necessary is:
Use SSL so that sessions can't be hijacked by eavesdroppers
Check the user's permissions before doing anything.
Plenty of sites do it similar to the way you described initially. For example, WordPress has URLs like https://example.com/wp-admin/post.php?post=112&action=edit. Clearly, a curious user could choose to edit the post=112 part.
So, one standard you might consider is: "Do I need to be more concerned about security and privacy than WordPress?"
If, for example, you don't want people looking at log files to know what IP addresses are editing what posts, you have a few options. Each approach has trade-offs so what the best one is will depend on what your biggest concerns are.
For example:
You might use a hash to conceal the post id number, like you suggest in your update to your question.
Or you might just send that info via a POST method (instead of GET) over SSL and not include it in your URL at all.
One advantage of the first approach is that people can use bookmarks to get back to the page. You might not want that. Or you might. Depends on your app.
One advantage of the second approach is that (for example) Google Analytics won't reveal if one post id is being accessed/edited over and over again or if many post ids are being accessed/edited. This may matter to you depending on whether such information might tell someone something and who has access to your Google Analytics stuff. Or it might not matter at all.
There are a lot of other possible considerations too, such as performance.
By the way, if you do use MD5, be sure to include something in the input that an attacker will not know. Otherwise, it will be trivial for an attacker to reverse a discovered hash via a lookup table and generate further legitimate hashes for sequential post ids. In PHP, you'd want to do something like:
hash('md5', $some_hard_to_guess_secret_string . $data_you_wish_to_hash);
There is no single best practice that applies to every situation. But in a typical situation, it is not necessary to hash the post id value or even send it through POST. In a typical situation, be sure to use SSL (so that sessions can't be hijacked) and check user permissions before doing anything and you are likely good to go.
You must treat all data coming from the client as suspect. This includes the URL. You should check that this client is indeed authenticated and that he is authorized to perform whatever action is indicated (by the URL, post data, etc). This is true even if you are only displaying data, not changing it.
It is not important if the record id is easily seen or modifiable in the URL. What matters is what can be done with it. Unless the id itself imparts some information (which would be surprising), there is no need hide it or obfuscate it. Just make sure you only respond to authenticated and authorized requests.
check permissions
don't use GET values for validation, authentication, authorization. session, post variables are ok.
to make things interesting... $x =md5(random number + post_id + userid) send all the values seperately like /edit/3?id=$x&y=rand_number when you get back to the edit page you check everything. else throw them an exception.
few more ideas involve db but if you are interested.
That's standard approach. You should alwasy check permissions on both: showing form and on action after submiting the form.
Regardless if you hash the ID or not, you must check permissions when editing a post, or someone could potentially stumble upon a page they are not supposed to be able to edit and they could cause serious damage. This could either be through randomly guessing, or through browsing through the history of another user that used your app.
Check permission before allowing someone to edit something.
That isn't to say you can't hash your IDs so they aren't quite as linear, but take a look at popular applications such as Wordpress, or even Stack Overflow. They are all based on incrementing numbers because regardless of knowing the ID or not, if you don't have permission, you can't edit it.
Obfuscating IDs will not increase security. As previously mentioned - you should always check permissions.
The reason why you might have an impression that you haven't seen url like this in a website that is concerned with security is because some of those websites are usually running on something like Java or .Net, and are using GUIDs ( http://en.wikipedia.org/wiki/Globally_unique_identifier ). Some of them however are using sequential IDs (e.g. gmail is using sequential IDs for emails).
MD5'ing is not a good idea. Cracking it is really easy, especially if it's something like md5(5684). I've looked up couple of hashes of numbers <100.000 here http://md5.noisette.ch/index.php and it found every single of them.
It can be better to use ACL for that. You can configure your application to deny everything and use ACL to give an access to the specific object.
It's a common practice not to use any hashes instead of ids in URL. Clean id allows you to grep apache logs, application logs with simple command. All logic must be in the code to give or deny access to the specific domain entity.
How much more secure do you need to be than checking if the user that's already confirmed who they are (logged in) has permission to edit the post in question? If you simply had a hashed value displayed in the address bar it would still be relatively easy to find the hashing algorithm and then they could still have control over what post they're trying to edit. Security through obscurity will always be a false sense of security.

Save and restore search parameters as unique ID

Noob question here. I'm overhauling some "Search" pages in a real estate website. I would like to be able to generate an unique ID (hash?) which contains in itself all the parameters of the search, e.g., the user would be given an URL in the form of http://search.example.com/a95kl53df-02, and loading this URL would repeat the exact same search.
Some of the search parameters are simply one of several options, some are integers, and there are also keywords (which I'll just append after the ID, I guess). What's the general approach to cramming this data into a string? I'm fairly comfortable with PHP/MySQL, but my practical experience is next to none, so I don't know "how it's done".
EDIT: I do not need the string to be random, and, indeed, I need the process to be two-way. Perhaps hash isn't the correct term, then. As for why - I'm doing this for the sake of brevity, since current URLs contain at least 22 GET parameters.
I have the nasty habit of always asking my questions on the Interwebs a bit too early, reconsiderations popping right into my head as soon as I have posted. I'm currently drafting a possible solution. I'm still open to any suggestions, though.
Hashes are not unique
A hash is NOT unique, you can't use it. Any hash can result from an infinite number of given strings.
You don't need randomness, just a unique token
You should just generate a unique token with the help of the database (even just an autoindexed id). You can create a cronjob that deletes old searches after a while.
That table would minimally contain the unique token plus the original search string.
Possible implementation
User does a search
Search params are stored in database, token is returned
Token is given to user in some way (e.g. do you want to save this search for later)
When user wants to repeat search with token, search string is retrieved from db and search run
You could use something like mcrypt() on $_SERVER['QUERY_STRING'], and then decrypt it if an encrypted URL is passed in. However, there are all sorts of problems here and I recommend not doing that.
Based on your edit that you are doing this because of a complicated URL, I would suggest that hashing is going to make the problem worse. If you have an error with the URL, you now have multiple places it could be going wrong.
Just make a random key that you then lookup in a simple flat-file database. You could check whether the URL is already in the database and then return the key if it is.
Another advantage of this system is that if your URL structure changes, then you can change all the URLs in the database and the users' short URLs still work.
Well to be random (which by the way you never can be), you can hash let us say the microtime (which is random-sh, since there is a low possibility that 2 users will search at the same time) along with some salt, with what you can use is the query id:
so something like:
$store_unique = md5(microtime().$queryID);
//the $store_unique you can save to the db with the query params
//then when anyone goes to the random url, you can check it against the db
UPDATE
Due to the comments below, I offer another solution (which can be more unique):
$store_unique = microtime(). "-" .$queryID;
//the $store_unique you can save to the db with the query params
//then when anyone goes to the random url, you can check it against the db

Categories