Encrypt/Encoding an ID in URL string - php

Just trying to do some security on my website and trying to figure out the best route to secure an ID.
EXAMPLE:
http://localhost/page.php?id=90
TO:
http://localhost/share/22349234987sdsdf9sdf87423498asf9
I am using HTACCESS to do the share part. But would like to hide the '90' and try to discourage anyone from just adding random numbers to try and receive a different response.
Any thoughts on how to create something like this, or if something already exists that works well with implementation?
Security is a factor, so just trying to find the best solution out there...

Hiding the ID is obscurity, not security.
If you want good obscurity, look at the mcrypt functions in PHP. Make sure to append a salt before encoding and decoding, otherwise it will be easy to guess the encryption/decryption.
And be aware that anyone else might still stumble across your URLs, defeating this entirely. I'd use some form of HTTP Auth over HTTPS if you want security, too.

A friend of mine implemented a method of signing all the GET requests with the current session token and a secret to prevent CSRF attacks.
But what you are trying to do is to have an URL that you can share with other people.
You could create an MD5 hash that resembles the original url, and save both in the database.
Now when /share/someLongId is opened, you can check in the database where to which URL that hash belongs and can redirect the user to that URL.
Another possibility is to use GUIDs instead of auto-incrementing IDs in the first place. That way all the IDs are just longer and not that easy to guess.

Depending on if you need it (the URL) to be persistent or not, you cold do either:
non-persistent: do something like this:
function getLink($id) {
$random = md5(uniqid());
$_SESSION['links'][$random] = $id;
return "http://localhost/share/$random";
}
echo getLink(10);
and then:
function getTrueId($id) {
if(isset($_SESSION['links'][$id]) {
return $_SESSION['links'][$id];
} else {
die("Unknown link!");
}
}
This will make links usable only to the current user in current session. If you need it persistent, you can generate random IDs and store them in the database along with real IDs, but this may be pointless, as the random ID can be used instead of real ID to do the same things...

Related

PHP - prevent users from play around with url GET variables

I have the following url
localhost/url/index.php?user_id=3
what is the best way to prevent site users from doing something like ?user_id=whatever, and i really dont want to use POST for some reasons in this case
Don't shoot querystring parameters ($_GET) directly into the database without some sort
of sanitizing (suggestion: use stored procedures).
Use some kind of authorization to protect resources
the user shouldn't be able to access.
Display a nice error page.
In this case you can use:
$_COOKIE['user_id'] = 10 or
$_SESSION['user_id'] = 10
In my opinion, Session would be better as you can log them out after every session of theirs and as said by #mehdi, it cant be edited by users, unlike cookies.
to use session, you will need session_start(); in front of every file
In addition to all that has been mentioned, since you are building the correct query string, etc. then include a hash of some sort that is a hash of all of the bits of data. The same applies when storing data on the client's machine as a cookie...
When you receive the GET (or read the cookie), use the data in it to re-calculate the hash, and stop/redirect/error out if the data no longer validates the hash check. When you do this, use some server-side salt that isn't displayed in the URL or in web page source, etc.
$user=3;
$urlGet="?userid=".$user."&uuid=".sha1($user."salt");
print("<a href='/index.php".$urlGet."'>click</a>");
Then when the link is clicked...
if(sha1($_GET['userid']."salt")!==$_GET['uuid']){
header("location: /index.php");
exit'
}

How to secure the id that are passed in a URL?

have a typical website that passed id values in the URL. ex. account.php?id=755
in the account.php page I do check the value
$id = 0;
if(isset($_GET['id'])){
$id = intval($_GET['id']);
}
if($id == 0){
echo 'this account does not exists!';
exit();
}
But, I am trying to find away to encrypt the value 755 prior displaying it and then decode it prior checking the value. So I am not displaying the the actual id but a mask.
My question is this:
1) Is this a good idea to encrypt and decrypt ids? so a mask will be passed and not the actual id.
2) is there an easy way of encrypting the ids by returning a string with a mix of number and alphabets only, using PHP?
But, I am trying to find away to encrypt the value 755 prior
displaying it and then decode it prior checking the value. So I am not
displaying the the actual id but a mask.
It seems like a very cumbersome idea. But if it means something to you and security, then you need to devise an encoding/decoding scheme that works for you. On the most simple level you can perhaps base64_encode the id and then decode it with base64_decode. The examples below are just illustrative. Please clean and adjust for your needs.
$encrypted_id = base64_encode($id);
Now to get it back, just run base64_decode:
$decrypted_id = base64_decode($encrypted_id);
But that is simple to hack.
A better way might be too create some secret “salt” to add to the ID that only your system knows or understands.
$salt="MY_SECRET_STUFF";
$encrypted_id = base64_encode($id . $salt);
In that way, simply using base64_decode is meaningless if the $id decoding does not factor in the salt:
$decrypted_id = base64_decode($encrypted_id);
It would only be usefully decoded with your salt factored into the decryption process:
$decrypted_id_raw = base64_decode($encrypted_id);
$decrypted_id = preg_replace(sprintf('/%s/', $salt), '', $decrypted_id_raw);
The logic is the raw decrypted ID still has the salt mixed in and the preg_replace would strip this out. Since only you know the ‘salt’ guess what? You are secure! But of course if you loose the salt for some reason, your whole system is useless because who knows what about your ids.
But again, this seems excessive unless protecting user IDs is the goal of your app and truly critical to security. If someone guesses an ID what is the worst that can happen? And how can you program against that ‘worst’ scenario.
There really isn't a point other than obfuscating the id's that you are passing. This isn't really any sort of security. An attacker can still guess at id's even if they are encoded.
As a rule of thumb, you should consider all of this information as public. If you are trying to prevent access, you should look into setting up a session.
Well one thing you can do to mask the actual id is to hash it. Example:
Generate the link:
<a href='script.php?id='<?php echo hash('sha256',$id); ?>'>click me</a>
And then on script.php reference hash('sha256',$_GET['id']).
Alternatively you can use mcrypt_encrypt and mycrypt_decrypt if you need more direct access to the actual value.
1- (Reply to the comments) GET allows URL reference and POST is as secure as GET. You can easily use a Firefox extension to modify the value at any time. Asking it to avoid the usage is like asking someone to add JS validation to protect a page. It will defend you against the regulars, not the smart ones.
2- I get that this looks like an homework and you might be limited in your options/will do do so, but you shouldn't rely on "step by step" validation. It's not that they don't work but they are often vulnerable when it comes to an attack if you forget to add something. They also give you a really messy code after you add a bunch of new elements.
Instead of trying to hide what pages your client is accessing, check who's the owner of the ID and who's trying to access it (with a server-side login)
3- If you just want something simple but cool, checkout base64_encode(), it's not actually secure, but it's an easy way to get the job done.
Since the url is account.php?id=755 i assume that you want to prevent the user from "spying" on any other accounts but their own. It wouldn't make sense to obfuscate the id in that case.
Store the id in the users session instead and make account.php retrieve it from there instead, so the user will only be able to see it's own account page.
Reasons why you shouldn't do this:
GET requests are cached and can be easily copy/pasted... resulting in the page you are showing being shared among users and allowing anyone else to access the page
You wont make it more secure
Why should should do this (can overrule the above):
You don't want to show customers orderNumber=xxxx for competitive reasons since competitors could guess your revenue
Solutions:
Change your order numbers to 1184xxxxxx
Use a reversible cipher (see comment at http://de3.php.net/manual/en/function.mcrypt-module-open.php)
Use MD5(orderNumber + SALT) in your database next to the order number

How to generate secure private urls in CakePHP?

I like to create a secure URL for a user for his entries (delete and edit links).
for ex, this is my actual URL
http://localhost/project/blogs/delete/1/test-title
what i want to do is,
http://localhost/project/blogs/delete/4324143563443/test-title (some hash made of salt+user auth id)
My main purpose is to create a secure hash along with the URL for delete and edit method.
Is there any custom method's available?
I searched in CakePHP Security functions http://book.cakephp.org/2.0/en/core-utility-libraries/security.html and not sure whether its the right way to do it or not sure which algorith to use)
Firstly, although I am not quite clear on how/why you want to do this, it sounds like you want to "protect" these links through obscuring their URL's. This is known as "Security through Obscurity" and is generally frowned upon.
In Cake (and most apps), the usual way to achieve this is to allow users to login (see: Auth Component) and then, for example, in your delete action (i.e. for the URL /delete/1) requests would be checked for a valid user session, and that the user has sufficient permissions to delete.
Although I would strongly reccommend otherwise, if you did wish to create these obscure URLs then you should probably use Security::hash();. The problem with this is that you wouldn't be able to just hash the id and then determine the id from the hash directly (thats the whole point!). Instead you would need to store the hashes in the database and then query for the hash (each post could have a unique hash generated either from the id or just random data, either would do).
As already mentioned "Security by obscurity" isn't very smart. Nevertheless easiest way to achieve what you want is use UUID's for your table's primary key instead of numeric auto increment.

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.

What is the best way to implement strong security standards for $_GET id's?

I am seeking the strongest security measure for people changing the IDs in the URL for comments, blogs, inbox etc...
Any suggestions?
Check the session permissions to see if they are allowed to perform the action?
If they're allowed to do it, then carry out the action. If not, then give them a 403.
Validating the data you get is a great idea, if you're expecting digit, make sure you get digits.
if(isset($_GET['id']) && ctype_digit($_GET['id']))
{
$id = $_GET['id'];
}else
{
$id = 0;
}
If your concern is people changing urls to see things, like requesting image 44 when you only wanted to show them image 42 you've got a few options:
Include various checks on the page to ensure the user has the appropriate permission to use the item.
Hash the item in the url to make items immutable.
Don't use sequential numbers, use random numbers or hashes.
If it's just an ID (numeric, I guess), all you have to do is validate it as an integer:
$id = (int) $_GET['id'];
Then you can query your database. You will get empty return sets when the ID does not exist and when it is invalid (because $id will be 0 in that case).
I'd imagine that digitally signing the get requests and appending that to the URL would work.
Sign it with a private key known only to your application, and then hash the GET variables and provide a signature in a &sig=blahblahblah.
That would probably work, but I don't really understand the need for protecting the GET variables. If designed properly, it really shouldn't matter what the GET variables are. A properly designed app shouldn't allow user GET variables to do anything damaging.
First of all, do not rely on $_GET for critical information. Always double-check whether the user has permission to view that comment id, blog id, whatever. As for ID filtering - simple intval() will help (but don't forget to handle 0's also)
maybe you find phpsec.org guide to php security, chapter 2, form processing interesting.

Categories