How to generate secure private urls in CakePHP? - php

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.

Related

Is it safe to secure URL parameters via a hashed key?

I want to let users share data outside of their account and have put together a proof of concept that essentially generates a unique URL with relevant URL parameters to display the data.
Obviously, a normal URL would simply let you modify the parameters, modify the query and extract any data you wished. So, with this, when the user generates a link to share data, I take the parameters, add a complex salt, hash the combined string (sha-2) and then use that as a key. So the URL might look like:
mydomain.com/app/shared.php?function=form&account=1&form=a19481e78dd87f5eb04afe94c85ea4f3&key=7dcaa38baa19e0f70262d8775582300346f5c544
When the URL is entered, the server recompiles the parameters and the secret salt and validates the key. If the key is not valid, no data is displayed.
I did think about further securing this by storing parameters in a DB, so the URL looks more like mydomain.com/app/h6Hs52ff2a, and the parameters never directly included in the URL, but equally I quite like the idea of generating sharable URLs on the fly with no DB backend.
I get the sense that the above approach might be a little frowned upon, but equally, unless you know the salt stored on the server (which is itself complex) I can't see any way of bypassing such a system.
Thoughts most welcome.
That's an entirely feasible approach, essentially a signed URL. The only weakness of that system is the secrecy of the salt/key; if you're using a fast hashing/encryption algorithm and a weak salt/key, it is feasible to brute-force the secret offline. So you'll need to use a strong (read: slow) enough algorithm to prevent that (a plain SHA2 is too fast!), and you need to ensure your key doesn't leak. You also need to ensure you don't lose your key accidentally, as that would reset all shared URLs. If this is done properly, it's a nice, stateless way to do things.
I'd look into JWTs as an alternative to your homegrown method, as they basically incorporate all your requirements already (they are essentially arbitrary signed data bags).
The advantage of the database approach is that it has no attack surface, and that you're able to invalidate shared URLs selectively. The drawback is that it uses database storage, which may have operational overhead.
One more deciding factor here would be URL-length, which you may or may not care about.
If you need reassurance, it is the same concept employed by Google Drive when you share document with a link.
Some comments:
Shared link readability. If the shared data is always about the same kind of model (for example account data), the link is OK. But if other types of data can be shared, like user details, I would include it in the URL so that the person getting the shared link can make some sense of the link.
If you are concerned with adding more security, you can enforce a key per user. You would need to keep the key in the database for each user.
When generating the url, add an expiry parameter and hash. When reading hashed link, you can then see if the link expired.
Make sure to use URL safe characters
Looking at what you are trying to achieve makes me wonder, why don't you simply:
map in your database a 128 bit random value to a set form parameters every time a user wishes to share a URL. It's as secure as the random number generator you use and the form values remain a secret (as they never leave the server).
encrypt the parameters using a cipher with message authentication (authenticated encryption with modes like GCM or OCB). Secure unless your keys leak. Hides the form parameters from the eyes of the users.
use only a part of 'authenticated encryption' like a message authentication code (MAC; HMAC). Does not hide the form parameters, adds a code that needs to be verified.

Including obscured IDs in the URL

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.

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.

ZF2: How to serve a secure image as part of a web page

I have built a ZF2 application which includes user profiles and I now want to allow users to upload and display their photo as part of their profile. Something like what you see in LinkedIn.
Uploading the photo seems easy enough (using Zend\InputFilter\FileInput()). I have that working fine.
It seems to me that storing them outside of the web root makes a lot of sense. (For example, I don't need to worry about user's using wget on the directory). But how do I then embed these images as part of a web page?
If they were within the web root I would simply do <img width="140" src="/img/filename.jpg"> but obviously that's not possible if they are in a secure location. What's the solution?
You're right. Web developers traditionally obfuscate the paths used to store images to prevent malicious individuals from retrieving them in bulk (as you allude to with your wget comment).
So while storing a user's avatar in /uploads/users/{id}.jpg would be straightforward (and not necessarily inappropriate, depending on your use case), you can use methods to obfuscate the URL. Keep in mind: There are two ways of approaching the problem.
More simply, you want to ensure one cannot determine an asset URL based on "public" information (e.g., the user's primary key). So if a user has a user ID of 37, accessing their avatar won't be as simple as downloading /uploads/users/37.jpg.
A more vigorous approach would be to ensure one cannot relate a URL back to its public information. A URL like /uploads/users/37/this-is-some-gibberish.jpg puts its ownership "on display"; the user responsible for this content must be the user with an ID of 37.
A simple solution
If you'd like to go with simpler approach, generate a fast hash based on set property (e.g., the user's ID) and an application-wide salt. For PHP, take a look at "Fastest hash for non-cryptographic uses?".
$salt = 'abc123'; // Change this, keep it secret, store it as env. variable
$user->id; // 37
$hash = crc32($salt . strval($user->id)); // 1202873758
Now we have a unique hash and can store the file at this endpoint: /uploads/users/37/1202873758.jpg. Anytime we need to reference a user's avatar, we can repeat this logic to generate hash needed to create the filename.
The collision issue
You might be wondering, why can't I store it at /uploads/users/1202873758.jpg? Won't this keep my user's identity safe? (And if you're not wondering, that's OK, I'll explain for other readers.) We could, but the hash generated is not unique; with a sufficiently large number of users, we will overwrite the file with some other user's avatar, rendering our storage solution impotent.
To be more secretive
To be fair, /uploads/users/1202873758.jpg is a more secretive filename. Perhaps even /uploads/1202873758.jpg would be better. To store files with paths like these; we need to ensure uniqueness, which will require not only generating a hash, but also checking for uniqueness, accommodating for inevitable collisions, and storing the (potentially modified) hash—as well as being able to retrieve the hash from storage as needed.
Depending on your application stack, you could implement this an infinite number of ways, some more suitable than others depending on your needs, so I won't dive into it here.
If you use Zfcuser, you can use this module:HtProfileImage.
It contains a view helper to display images very easily!

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.

Categories