I am trying to create a folder structure like so:
Uploaded files
a
aaron.doe#hotmail.com
b
c
...all the way to z
one level ABOVE the public web directory. The only unique key (besides the user_id itself) is the user email, since their email is their username, so...
Question: Would people be able to access these directories and get a hold of all user's email address? How bad of an idea is this? What possible alternatives do you suggest?
Thanks.
Definitely make sure you use hashes instead of plain-text E-Mail addresses. That is a must.
Other than that, I guess this is as safe (and unsafe) as a solution can be that is based on security through obscurity (i.e. your security relies solely on the fact that nobody knows the URLs - but if they do, they can access them without limitation.) There are many potential holes - a user could bookmark a URL; it could be embedded somewhere on a page; it can be stored in server, browser, and proxy logs...
Take a look at the PHP dir function:
http://php.net/manual/en/class.dir.php
If you want the folders to be publicly accessible to your users via the web, why put the folders above the web root?
Also, you may consider using some sort of hash for the folder names, 1) because nobody wants their email addresses publicly revealed, and 2) revealing internal user_ids could lead to exploits.
As Pekka just replied; it might be a good idea to hash the email addresses since there's a big chance that the links might be posted to a forum or similar, and the post might then get crawled by an email address crawler. I think that just a simple hash (e.g. md5) of the address would (almost) solve this.
See this thread on how to prevent directory listing, and what to do if the Apache way doesn't work.
Hash the e-mails to use as folder names and put it above web root.
You can use a simple autentication to give access to this files and a php file to read them and send
them to the browser.
Related
For example if you had an online communitity that allowed the sending of private images between members, like a digital penpal or dating website.
What would be the best practice for securing these images on a webserver and the best practice for displaying them to the authenticated user?
Here is what I have done so far:
Store Images outside of public root.
Retrieve images via one time code instead of the actual image location.
Randomised hashed image names and folder names that are not easy to guess.
PHP script to authenticate user before displaying the image.
Outside of root seems to be one of the best ways to store the images to make then hard to access, but what about if the server itself is directly hacked into?
Is there a way to hash and salt the image files so it can only be displayed once the hash and salt matches, even if a hacker had the file?
Would this be possible to return via PHP or SQL?
I was thinking of encoding the images to base64 and salting the base64 with a salt generated from a randomly generated password per user (Is this possible?)
Or is there a better method?
For a basic protection, the things you have described could be enough, maybe even too much in the sense that if folders are outside of www root, randomizing folder names won't add much to security but will increase complexity.
Based on a risk assessment that you should conduct for your scenario, you can choose to do more. Of course if you find that you can lower the risk of a $100 breach with the cost of $10000, you probably don't want to do that. So do the maths first. :)
I can see two major threats to your solution, one is a bug in the access control logic that allows a user to download images that he was not supposed to be able to access. The other is an attacker gaining access to your web server and downloading images (as your web server needs to have access to image files, this is not necessarily root/admin access, which increases the risk).
An idea one could think of would be to encrypt images on the server. However, with encryption, key management is usually the problem, and that is exactly the case now. There is not much point in encryption with a key that your application can access anyway, as an attacker could also access that key in case of a successful application level attack (and also in case of a server/OS level attack, because the user running your web server and/or application must have access to the key).
In theory, you could generate a public/private keypair for all of your users. When somebody uploads an image, you would generate a symmetric key for the image, encrypt the image with that key, and then encrypt the symmetric key with each intended recipient's public key and store encrypted keys (and metadata) with the image. The private keys for users should also be encrypted, preferably with a key derived from the user's password with a proper key derivation function like PBKDF2. One implication is that you can only get the user's private key when the user logs in, because you don't store his password, so that's the only time you have it. This means you would have to store your user's decrypted private key in server memory at least, where it is not really safe (and any other store is much worse). This would still provide protection against offline attackers though (somebody having access to backups for instance), and it would also limit attack scope to victim users that log on while the server is compromised (meaning after it is compromised, but before you realize this). Another drawback is the complexity of this solution - crypto is hard, it would be really easy to mess this up without experience. This would also mitigate the threat posed by an access control flaw, because unintended images could not be decrypted with the logged on user's private key.
A completely different approach would be to separate your application into several components: a logon service (similar to SSO), your web server, and a backend image service. When your user logs on to the authentication provider (AP), he would in this case receive a token with claims, signed by the AP. When talking to the web application, he would use this token for authentication. What differentiates this solution from the previous is that when a user requests images, the web application would pass his token to the image service, and the image service could on the one hand store images securely on a box not directly accessible from the internet, and on the other hand it could authorize whether for the token received it wants to return images (it could verify the token with the AP or by itself, depending on the implementation you choose). In this case, even if an attacker compromises the web application, he would still not be able to produce (sign) a valid token from the AP to get access to images on the image service, and it could potentially be much harder to compromise the image service. Of course in case of a breach on the web server, the attacker would still be able to observe any image flowing through, meaning any user that logs on while the server is compromised would still lose his images to the attacker. The added complexity of this solution is even worse than the previous one, which means it is easy to get this wrong too, and it's also costly both to develop and maintain.
Note that none of these solutions protect images from server admins, which may or may not be a requirement for your app.
I hope this answer sheds some light on the difficulties involved in making it significantly more secure than your current solution. Having said all this, implementation is key, and details (the actual code level vulnerabilities) probably matter the most.
You have these listed as some of your security protocols:
"1. Store Images outside of public root.
2. Retrieve images via one time code instead of the actual image location.
...
4. PHP script to authenticate user before displaying the image."
This should be enough, but then you mentioned...
"3. Randomised hashed image names and folder names that are not easy to guess."
If you are actually doing the first two correctly, 1 and 2, then it's not really possible for 3 to have any effect. If the images are outside of the webserver directory, then it doesn't matter if the folder names and image names are easy to guess.
Your code will look like (for doing 1 and 2), assuming the environment is the root directory of your webserver (i.e., example.com/index.php)...
$file_location = '../../images/' . $some_id_that_is_authenticated_and_cleansed_for_slashes . '.jpg';
readfile($file_location); // grabs file and shows it to user
If you are doing the above, then 3 is redundant. Hashing the names, etc., won't help if your site is hacked (the Apache security is bypassed) and it won't help if your site isn't doing the above (since users can then just directly access the URLs). Except for that redundancy, the rest seems perfect.
Possibly all below is not reasonable, but want to make clear and understand.
Aim is protect email addresses and phone numbers, recorded in mysql. If hacker get database, to make it more difficult to get real emails
Decided to do it like this
1) outside webroot place some text file
2) in php $content_of_some_file = include '/wamp/some_file.txt';
3) INSERT into users (email) VALUES (AES_ENCRYPT('someemail#example.com', $content_of_some_file));
But if hacker can access to mysql, no problems to get access to php files and use echo $content_of_some_file;. Get key and SELECT AES_DECRYPT(email, 'key')
Any ideas how to prevent it? Is it possible at all? If not possible, then all above is almost unreasonable...
Aim is following:
1) visitors from website want to send message to some user
2) visitor types message in input field, clicks send
3) i take user's email from mysql and send the message to the email
Want to prevent:
Hacker get content of mysql and want to take emails to send spam (instead of actual emails hacker see something not understandable). Hacker wants to convert to actual emails.
Hacker also gets access to php files. In one of php files hacker inserts code to see $content_of_some_file (a key placed outside webroot).
Using the key, hacker AES_DECRYPT and get actual emails.
Is it possible not to allow hacker to see $content_of_some_file (a key placed outside webroot)?
Tried to use php DEFINE
If in file outside webroot insert define("GREETING","Hello you! How are you today?");
And in file inside webroot insert only echo constant("GREETING");
I can see content of constant GREETING. So, if hacker inserts such code, he can see content of key for AES_DECRYPT and decrypt emails.
Seems must set php files (directories) not writable... Hacker could not insert code and could not upload files. And could not directly access to file located outside webroot. It would be some measure...
And seems also need obfuscate part of php code so that from code hacker can not know variable and file names.
If contents of some file is just a salt as it seems above, you could just use a salt (a random collection of characters) located anywhere.
That way, the hacker in question would have to be in your PHP code as well as having database access. In terms of actual usage; the salt would have to have some kind of reference if using a list.
define('SALT', 'iuoerghiuowerbnfcuioq3hrj980127yu589734754-12j84903q24fyjrm03qwy4ruw');
INSERT into users (email) VALUES (AES_ENCRYPT('someemail#example.com', SALT));
As salt is an unchanging password, the entry in mysql can be returned to a readable for using AES_DECRYPT along with the salt using
SELECT AES_DECRYPT(email, SALT)
WHERE user_id = 'recipient_id'
FROM users
Limit 1;
Do note that in both these cases you will need to escape the mysql string in order to insert the actual salt as a password, otherwise you will literally be using SALT as the password.
The other alternative is to use a ready made class such as the one found here which will allow you to encode the entry from within PHP completely ignoring the database.
Update to reflect holding salt outside of web root
In order to allow PHP to access paths outside of the web root, you will need to look up the basedir directive. For example:
open_basedir = /srv/http/:/path/to/where/you/keep/salt/
This declares both the web_root and another path as being accessable by PHP, note the semi-colon in between. Now, you create a file in that path with your salt:
/path/to/where/you/keep/salt/salt.php
<?php $strSalt = 'hideouslylongandobscurestring'; ?>
With that set, where you intend to use the code, instead of a define as used earlier, replace with the following:
include('/path/to/where/you/keep/salt/salt.php');
INSERT into users (email) VALUES (AES_ENCRYPT('someemail#example.com', $strSalt));
However, if the hacker in question has access to your files they can simply find this include and extract the salt. There's really no simple panacea (cure all) if both your database and host get compromised.
If certain you need something a little more foolproof and willing to go to extremes, you're really looking at a setup where you have the salt generated offsite (based on user account criteria) and delivered through secure means by something like OAuth for the authorisation part. At this point, you're really starting to make life difficult for yourself when you just want to encrypt an email address though and going beyond the scope of this question.
Hope this food for thought helps
I'm using a simple service called I'm PDF to create PDF versions on online invoices. Right now, the client has to login to see the online version, but as a result, the api service doesn't have access to it either. How would I detect that the pdf service is accessing the site and allow it accordingly without a password?
I thought $_Server['http_referrer'] would do it, but I didn't have any luck. I wasn't sure even what the variables would be, so I had a var_dump($_Server)it emailed to me, whenever the page was visited. It sent it beautifully when I went to the page, but when the pdf was generated, no luck.
Any ideas?
Thanks for your help!
You need to identify the user. The only way with this service seems to be to append something to the pdf URL (GET parameter).
So create some secret on the server side, sign user id with it, append it to impdf URL and parse it with your secret on request. You will end up with user id that you can use for authorization.
Have you tried looking at the IP address?
The service probably accesses the site from the same IP address (or range of addresses). If that's the case, then all you have to do is check the remote IP address of the request, which I believe is in $_SERVER['REMOTE_ADDR'] for PHP.
I should also mention that IP address ACLs are not quite perfect, though, because IP addresses can be spoofed. They probably should not be too heavily relied upon for security unless there is absolute control over (or guarantee of safety) of packets leaving and entering the network. That said, they are most likely sufficient for your "simple" security requirements.
The HTTP referer is pretty much trivial to circumvent. As such, it is not reliable enough for security and should be taken with a grain of salt.
I wouldn't go for any of the above mentioned advices. Basically they all bring down your security measures.
I'd use an API such as PDFmyURL's API, which allows you to login directly via a host of standard ways. This makes sure you don't have to build anything new.
Direct link to section on accessing secure area: PDFmyURL API documentation
Disclosure: I work for Kaiomi, which owns this service.
My requirement is i have a portion of the site that should allow user to access from only one system. He may used different IP's and different by if the system is same he should be able to get the access. My site is in PHP. Doing some R&D i found so JS which helps to get the MAC address if the visitor is using IE. My case is i cannot restrict user to use IE. He may use any browser. Is there any way to get the MAC address. If not possible how should i restrict the same user from accessing content from different computers.
No. MAC addresses are not useful beyond the local network, so they're not available in the upper layers (eg. Application layer). Using Javascript to get the the address isn't exactly foolproof either, since it's incredibly easy for someone to manipulate.
If you want to restrict the hosts that a user can access your site on, you'll need to use a method of storage persistence within the browser to store a key - something that uniquely identifies them. Some forums use this method to catch people attempting to bypass their bans.
You should generate some kind of random key (or encrypt a string), store it on the server-side along with the user you want to identify, then give the user the key to be stored in a cookie/localStorage.
Though, there is the obvious problem of a user clearing their cookies...
Hopefully that gives you a bit of head-start.
Daniel is correct, it's not practical to attempt to use MAC addresses.
For your purpose, you'll probably want to use a few layers. Cookies are a good place to start. As stated though, they can be cleared. The user also may have cookies turned off. Cookies are also stored in plain text on their computer, and they're really easy for the user to modify.
You could also implement sessions with PHP. Each session is unique for each system, and browser. If the user switches browsers or restarts their current browser, the session is re-created.
You could also check their user-agent string. It will be different for each browser, and "most likely" each system, but it can easily be spoofed.
You could also use a public/private key exchange, but that can be a pain to implement and manage.
There really is no one fool proof solution. The best bet is probably to use a combination of techniques to get it as "close to foolproof" as you can. Just try not to make it too inconvenient for the user.
I have a PHP file i made that basically give me passwords to all my users. I want to be the only one able to view the contents and see the page. Whats the best way doing it?
Password protection? Requiring a special cookie that only I have?
Give me some ideas..
I'd recommend that you stop storing passwords and store the hash of the password instead. Even you shouldn't really know your users' passwords.
What you're doing isn't even authentication or authorization. At best it's identification. If you're hell-bent-for-leather on doing it, what Chacha102 said, plus you'll also want to chgrp it and chmod it so that only the internet user and your user can view it.
If you want to be able to see if via a browser, try these:
Look into WWW Basic Authentication, which will basically have the browser prompt you for a username and password.
http://www.htaccesstools.com/htaccess-authentication/
http://eregie.premier-ministre.gouv.fr/manual/howto/auth.html
If you have a static IP address, you could make sure that only your IP address can see the page:
if($_SERVER['REMOTE_ADDR'] != '192.168.1.1')
{
die();
}
If it isn't suppose to be seen by a browser, The BEST Solution would be to put the file above the DocumentRoot. AKA:
If your index.php file is at /Path/To/Root/Public_HTML put the file in /Path/To/Root
Don't store your users passwords in plaintext, hash them in the database.
Since I'm assuming you need the functionality of logging in as a user, I would suggest creating a script that let's administrator accounts (you can identify that however you want) log in as any user.
If you're storing all the data in a location that's under the wwwroot, then you risk downloading of the file, whether by bad configuration of by security vulnerability. It is also possible that this solution includes hard coding of users and passwords, which makes password rotation more difficult. And if users can change values in the file, you've got to be extremely careful that they can't inject PHP code into the password file, or they'll be able to take over your application. And the ability of an administrator to see cleartext passwords is considered a bad practice, and should be avoided.
The modern best practice is to not do it that way, if at all possible. Store the data in a location from where the web server does not normally allow direct downloads (such as outside wwwroot or in a database where you've protected against SQL injection issues), implement an authentication and authorization scheme, and rely on that scheme to control who's allowed to do what.
Check out www.owasp.org to get more details - it's a great starting point.