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
Related
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.
I need help on coming up with a strategy to handle object ids in a PHP/MySQL application I'm working on. Basically, instead of having a URL look like this:
/post/get/1
I'm looking for something like:
/post/get/92Dga93jh
I know that security-through-obscurity is useless (I have an ACL system in place to handle security) but I still need to obscure the ids. This is where I'm stuck.
I thought about generating a separate public id for each DB row but have been unable to find a way to create truly unique ids.
I suppose I could encrypt and decrypt a MySQL auto increment row id as it leaves and enters my app, but I'm not sure how 'expensive' PHP's encryption and decryption methods are. Additionally, I need to make sure that the obscured id remains unique so that it doesn't decrypt into the wrong value.
Also, since my domain objects are related to each other, I want to avoid any unnecessary strain on MySQL if I decide to go with generating and storing an obscure id in the tables.
I'm beating my head against the wall because I feel like this is a common scenario, yet can't figure out what to do. Any help is greatly appreciated!
I'd just use a salted md5. It's secure for 99% of the cases. The other 1% will be when you are wacking your head on the wall cause you got your data stolen by a pro-hacker and it becomes critical to minimize the impact of it.
So:
$sql = 'SELECT * FROM my_table WHERE MD5(CONCAT(ID, "mysupersalt")) = "'.$my_checked_url_value.'"';
And generating the same thing from PHP can be done using similar strategy:
link text
Hope this is what you're looking for..
As long as you given 9-char base62 string - you could follow this strategy:
Generate a number from 1 to 13537086546263552 (62 ^ 9)
Convert it to the base62 string
Try to insert to the database (you're supposed to have the unique index over id field)
If ok - do nothing
If not ok - repeat 1-3
Use a one-way hash like md5, etc.
Depends on the application really, if its super essential that you have IDs from which the user can never 'guess' the original IDs, then use a recursive call to db to generate a unique public ID.
If on the other hand, you just need the IDs to look different without any security worries if someone can 'guess' the original ID, and are concerned with the performance, you can come up with a quick and basic math equation to generate a unique id on the fly and decode it as well when the URL is accessed.
(I know its a HACK, but gets the job done for a lot of cases)
E.g. If I access /blog/id/x!1#23409235 (which means /blog/id/1)
In the code, I can decode above by:
$blogId = intval(substr($_GET['id'], 4)) - 23409234;
and of course, while generating the URL, you add 23409234 to the original URL's id and prefix it with some random char bits..
Oh and you can use Apache's mod_rewrite to do all these calculations.
The probably easiest way is checking whether there is already such a record in a
do {
$id = generateID();
}
while(idExists($id));
loop. There shouldn't be to many duplicate IDs so in most cases there are only two queries most the time: Checking and Inserting.
I'm looking to store (in mySQL) an obfuscated version of a phone number, where the number is used for authentication (I need to be able to get back the original number).
I've thought about an arbitrary scheme like storing the number * 15 or some constant only my app knows.
What are some better ways of doing this?
EDIT: Some things I'd like to clarify:
The phone numbers that are saved can be used to log into an iPhone app - so I want users to be able to see which number they have connected to the service incase they want to log into the app with a different number later. This means I cannot hash the value.
Essentially I am looking for a way to protect the data if someone lifts my database that they don't have a bunch of phone numbers in raw form. So I'd like to obfuscate them so I can use them for authentication, but be able to get one back in its original form without storing it raw.
EDIT: To clarify, I am not authenticating on JUST the phone number. If implemented, it would be phone number + a password! Enter a single string of digits that may exist and you're in? lol - my apologies if I have misled some folks.
Store where? In a database? Use an encryption function rather than rolling your own system.
In MySQL it'd be as simple as:
INSERT INTO users (phone) VALUES (AES_ENCRYPT('yourkey', '867-5309'));
of course, now you're changed the problem from hiding the phone numbers to "where the #$##$## can I hide this key?". Obvious solution: hide the key under a rock outside your server's front-door. Which changes the problem into "where the ####$###% can I hide this rock?". Obvious solution: cover your front yard with a steel cage with a padlock on the door. New problem: how to hide the padlock key... and so on.
How about actual encryption? In this scenario, a good symmetric encryption algorithm is trivial, since the length of the payload is limited to, what, 10 digits, so you can get by with a key that's also 10 decimal digits long; using such a key, all you need to do is something like XOR or increment / mod 10 on each digit. Of course, the weak link in this scheme then is the way you store the key.
I am curious, however, why you need to get them back out - if it's for authentication:
you shouldn't be using phone numbers, as these are easy to look up, even automatically
you should be storing secure one-way hashes with individual salts, so you couldn't even get them back out youself if you wanted to (except by brute-forcing)
Using the Cipher Class you can do this:
$phone = '...';
$key = 'secret.for.each.number';
$phone = Cipher::encrypt($phone, $key);
Before you store it in the database. Then later you can pull it out and do this:
$phone = Cipher::decrypt($phone, $key);
A better way would be not doing that. There is a reason one-way encryption is used to store passwords.
If you need to get back the original value, you should not be using it for authentication, since it will invariably be easy for an attacker to find it.
If you feel you need to hide the value by obfuscating it, you probably need to change something fundamental about how you're storing the data.
This isn't a very good approach to security. Several things jump out at me:
Phone numbers are very easy to guess: just program something to start guessing random combinations. Encrypted or not, your program is validating using these numbers, so it will eventually work on some. You need an extra layer of security like a password known only to the user in question. I would recommend anti-brute-force attack measures as well.
Any two-way encryption can be cracked, it is as simple as that. If you need to be able to decrypt data in the database easily, the only benefit from encrypting it is if someone hacks into your database and grabs the information. As others have pointed out, if that happens, you have bigger issues. The other scenario is for staffers who could have valid access to the DB. If you are hiding the data from them, it is important to encode the information in some way. But multiplying the phone number by a "unknown" constant is not ideal. Use a better method.
Surely I know my friend's numbers, so I could hack into anyone's account, correct? You need to add a password component if you haven't already. The password should be 1-way encryption using a strong and unique SALT. Once added, you only need to encrypt phone numbers in the DB if you don't want your staffers to see them. Otherwise you are wasting time encrypting them.
There is no point in this question.
Just leave these phone numbers as is. You will gain no security improvement from such obfuscation
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...
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.