Solved

PHP Security - How do protect my chart.

Posted on 2009-05-12
23
302 Views
Last Modified: 2012-05-06
I'm looking for ideas.

Server = Linux.

Problem

1. I have a form that posts back to itself

2. The form has a JPGraph chart

3. After post back the src of an image on the form is set to the JPGraph chart with the post parameters echoed like this:
www.host.com/jp-chart.php?var1=12&var2=14

4. The application operates in a members area.

5. How can I protect the chart so that non-members cannot access the chart?

As the application stands anybody could right click and view the path and variables required to make the chart work therefore bypassing the login process.
0
Comment
Question by:lwfuk
  • 11
  • 6
  • 5
  • +1
23 Comments
 
LVL 19

Expert Comment

by:Michael701
ID: 24366606
You probably set a cookie whent he user logs into the member area.

Just make sure it's set when the jp-chart.php program executes. If it's not set, send them to a login area.
0
 

Author Comment

by:lwfuk
ID: 24366710
That would be easy to hack.

Thanks Anyway.
0
 
LVL 19

Expert Comment

by:Michael701
ID: 24366918
why would that be an easy hack? how do you verify that a member logged in?
0
 

Author Comment

by:lwfuk
ID: 24367013
Easy - Copy it.
0
 
LVL 18

Expert Comment

by:Hube02
ID: 24367513
As Michael701 says,

I generally use session variables to verify login, as well as to check permissions, never use cookies to store user information.

In the php script that generates the graph I would do the same checking that is required to access the page that contains the link. If your login checking is secure for the page then it should be just as secure for the script that is called in the link/image/whatever.

You could even create a "login error" image that is returned if the user is not logged in.
0
 

Author Comment

by:lwfuk
ID: 24367569
Many thanks Hube02. The only problem with this method is that a registered user could still get direct access to the graph once they had established a session. I want to stop direct access to the graph as well as stop external visitors from getting to it.
0
 

Author Comment

by:lwfuk
ID: 24367583
PS Hube 02.

Michael701 recommended using text based cookies not sessions.
0
 
LVL 19

Expert Comment

by:Michael701
ID: 24367618
I do a triple check on one of my sites.

When the user logs in: I create a session / cookie with user name. also I generate a random number and store it as a cookie called checksum, I then write this value to the user's record along with the IP address.

To verify the user is valid, I get the session / cookie user name and checksum, then read the user "select username from users where username='cleaned username data' and checksum='cleaned checksum data' and last_ip='cleaned ip data';

if no records are found then it's not a valid user
0
 
LVL 19

Expert Comment

by:Michael701
ID: 24367702
Session variables can be used as log as you don't mind users being logged off after the 20 minute session timeout.
0
 

Author Comment

by:lwfuk
ID: 24367758
Many thanks Michael701.

This would still allow a registered user to access the chart directly.

OSCommerce has a secure file download facility for merchants who sell electronic products.

You can be a registered user and still you're not allowed to download a file until you have paid for it.

The download directory has a .htaccess file as shown.

I wonder how they override it to enable and disable downloads.

AuthType Basic

AuthName "No access"

AuthUserFile .htnopasswd

AuthGroupFile /dev/null

Require valid-user

Open in new window

0
 

Author Comment

by:lwfuk
ID: 24367792
Michale701.

Session variables are not suitable. A registered user could access the index page, setup a session and then go directly to the chart and use it as often as they like.

I did a test.
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 19

Expert Comment

by:Michael701
ID: 24367930
Ah, well that's a whole new ballgame.

This is normally done by having a download.php program that first verifies the user and then does a forced download of the data. This way the actual data file path is never reviled to the clients machine.
0
 

Author Comment

by:lwfuk
ID: 24368075
Sounds interesting Michael701:

To show you are right though - could you produce a short test script that would force and image download with the htaccess file above. Assume that there is a directory called charts with the htaccess file above and a single image called chat.png. Outside there is a file called download.php.

ie:

/download.php
/charts/.htaccess
/charts/chart.png

When I go to /charts/chart.png I will be rejected by the ht access file.

When I go to download.php I will either see the image or be presented with a download popup

0
 
LVL 19

Expert Comment

by:Michael701
ID: 24369490
try this
<?php

$strFilePath = "charts/chart.png";
 

header("Content-Type: image/png");

header("Content-Length: ".filesize($strFilePath));

header("Content-Disposition:attachment; filename=something_different.png");
 

echo file_get_contents($$strFilePath);
 

?>

Open in new window

0
 
LVL 12

Expert Comment

by:jahboite
ID: 24372880
You could try setting a session variable to a unique random token such as:
sha1(uniqid(rand(), TRUE))

when the form is requested and set the src of the JPGraph to include this token in its paramaters. Then when the JPGraph is requested, you check that the token in the request is present and is the same as the session variable.  If it's present and they match, unset the session variable and process the request. If they don't match, reject the request.
If the user then tries to request the JPGraph again, directly or otherwise, the token won't match the session variable and the request can be rejected.
0
 

Author Comment

by:lwfuk
ID: 24375122
Here is how I solved it.

Here is the directory structure.

/test/index.html
/test/charts/.htaccess
/test/charts/chart.php

The code is below.

To test:

1. Go to your test directory and you will see a black rectangle
2. Go to test/chart/chart.png and you will get a 403 error
3. Disable .htaccess and repeat 2 and you will see the black rectangle

Can anybody hack it?

# .htaccess
 

# Below I am blocking just PHP files but you can block any type of file.

# For example, the line below blocks direct access to jpg|jpeg|gif|png|php

# <FilesMatch "\.(jpg|jpeg|gif|png|php)$">
 

<FilesMatch "\.(php)$">

    SetEnvIfNoCase Referer "^http://([^/]*\.)?your_domain.net/" local_referrer=1

    Order Allow,Deny

    Allow from env=local_referrer

</FilesMatch>
 

<!-- index.html -->
 

<html>

<body>

<img src="chart/test1.php" />

</body>

</html>
 

//chart.php
 

<?php
 

//Create a black rectangle.
 

header('Content-type: image/png');

$im = imagecreatetruecolor(400, 400);

$text = 'Hello';

$black = imagecolorallocate($im, 0, 0, 0);

imagefilledrectangle($im, 0, 0, 400, 400, $black);

imagepng($im);

imagedestroy($im);
 

?> 

Open in new window

0
 
LVL 12

Expert Comment

by:jahboite
ID: 24375497
Yes, very simply by sending the correct Referer header (via the browser, you could use something like the tamperdata add-on, or you could use wget --referer).

Your solution is not a very robust one, but if it works for you then all well and good.
0
 
LVL 12

Expert Comment

by:jahboite
ID: 24375636
.. and by way of illustration, a screenshot of the direct access using tamperdata to add a referer - this is why hotlinking is so trivial to circumvent.
ref.png
0
 
LVL 12

Accepted Solution

by:
jahboite earned 500 total points
ID: 24375900
Try these two files index.php and chart/chart.php (forget the .htaccess)

You should be able to refresh the index as many times as you want and each time a new url paramater will be generated for the img src.
You should not be able to request chart/chart.php directly even with a valid token.

When the index is requested, the session variable is created and placed into the img src.
When the browser loads the index, the image is requested using the same paramater.
When the image is created, the paramater is checked against the session variable and if they match, the session variable is destroyed (so it can never be reused) and the image is displayed.
If they don't match, the image isn't displayed - or rather, an empty image is returned (we just exit the php script).

Thus the only way to see the image is to request the index.
// index.php

<?php

session_start();

$_SESSION['token'] = sha1(uniqid(rand(), TRUE));

$url = "chart/chart.php?t={$_SESSION['token']}";

?>

<html>

<body>

<img src="<?php echo $url ?>" />

</body>

</html>
 

// chart/chart.php
 

<?php
 

session_start();

if ( !isset($_SESSION['token']) || !isset($_GET['t']) || $_SESSION['token'] != $_GET['t'] ) exit;

unset($_SESSION['token']);
 

//Create a black rectangle.

header('Content-type: image/png');

$im = imagecreatetruecolor(400, 400);

$text = 'Hello';

$black = imagecolorallocate($im, 0, 0, 0);

imagefilledrectangle($im, 0, 0, 400, 400, $black);

imagepng($im);

imagedestroy($im);
 

?>

Open in new window

0
 

Author Comment

by:lwfuk
ID: 24376324
jahboite

I love your solution and you deserve all of the points which I will give you.

I noticed the issue you mentioned but I put it down to disk caching.

I'm going to do a quick test with a random number generator on the image and see what happens.

I'll report back and award you the points before shutting down the issue.

Many Thanks,

Adrian Smith
London



0
 

Author Comment

by:lwfuk
ID: 24376511
Hi Jahboite,

As I thought it is disk caching (which is OK in my application but your solution is the most robust).

Replace the code in chart.php and you will see that the random number (10 exp 11) in the index file and the chart file is identical. The probability against this happening is (10 exp 22).

I'll leave the issue open for a while incase you want to reply.

If not I'd like to thank you for a very intelligent response.

Kind Regards,

Adrian Smith
London
<?php

// Set the content-type

header('Content-type: image/png');
 

// Create the image

$im = imagecreatetruecolor(400, 30);
 

// Create some colors

$white = imagecolorallocate($im, 255, 255, 255);

$grey = imagecolorallocate($im, 128, 128, 128);

$black = imagecolorallocate($im, 0, 0, 0);

imagefilledrectangle($im, 0, 0, 399, 29, $white);
 

// The text to draw

$text = rand(1, 100000000000);

// Replace path by your own font path

$font = '../../chart/truetype/arial.ttf';
 

// Add some shadow to the text

imagettftext($im, 20, 0, 11, 21, $grey, $font, $text);
 

// Add the text

imagettftext($im, 20, 0, 10, 20, $black, $font, $text);
 

// Using imagepng() results in clearer text compared with imagejpeg()

imagepng($im);

imagedestroy($im);

?>

Open in new window

0
 
LVL 12

Expert Comment

by:jahboite
ID: 24377701
Hey Adrian,

I just wanted to comment on the solution you gave and to expand upon why it's not a robust one and, additionally, why it's possibly a problematic one.  I'm not sure whether you fully understand what it does/doesn't do so please forgive me if I'm going over old ground ...

Your solution is a common method to prevent direct access to a resource (your chart image) and enforce it's retrieval via a linking page at your domain.
The FilesMatch directive in the .htaccess you gave is simply checking that there is an HTTP Referer header in the HTTP request (from the visiting users browser) and that the domain part of the referring URL matches your domain.
This is a method to determine whether the request was made by typing a URL into the address bar (or similar means) and directly requesting the resource or whether the the request was generated indirectly either by following a hyperlink or by requesting a linked resource (e.g. <img />).
The idea is that if the Referer header is present in the request, it must be an indirect request and if the domain part matches your domain then the linking resource must be one of your pages - and only then do you want to permit access to the resource.

The problem here is twofold.
1) There are legitimate reasons why the Referer header might not be present - it might be a privacy concern that prompted a user to disable it's use in their browser or it the header may have been stripped from the request by some kind of proxy.  In these cases, you may be denying access to legitimate users.
2) Your security is reliant on a variable which is completely under the user's control.  As I hoped to demonstrate with the screenshot above, the user can very easily inject the Referer header to make a direct request for your resource look like an indirect one and thus accessing the resource directly - which is exactly what you wanted to prevent.

This has nothing to do with caching at all.  It's never a good idea to "protect" a resource based on something the user can control (in fact it's a very bad idea).  Having said all of that, if it's not a major security issue if someone directly access the chart (given that they are authentcated users as you suggest in the original question) then your solution should work to prevent some users from doing so, but anyone who actually wants to find a way to access the chart directly will not have a very difficult job on their hands.  And bear in mind 1) above too.

The solution I presented in no way sought to prevent unauthorised access to the chart, for this you need to make the same authentiction checks in jp-chart.php as you do in any of the php files in the members area - in addtion to the token method I presented whose aim was only to prevent direct access to the resource.

Finally, I couldn't reproduce the identical random number issue, but then I didn't quite understand what you meant by
"the random number (10 exp 11) in the index file and the chart file is identical"
since only one random number is being generated - in chart.php

0
 

Author Comment

by:lwfuk
ID: 24395741
Hi jahboite

Sorry. I missed your response - for which I am very grateful.

I just revisited this solution because I'm having trouble using the charts in a PDF file and I decided to adopt your solution.

Many thanks for the detailed response you gave and I have learned something very valuable from you. I had never heard of Tamper Data until I met you and I have always wondered if there was a way of fiddling the http referer header. Now I know. Many thanks.

The (10 exp 11) issue just means that I was generating a random number between 1 and 10 to the power 11. The chance of getting identical numbers in both situations is 10 to the power 11 multiplyed by 10 to the power 11 which equals 10 to the power 22. ie 1 in 1 000 000 000 000 000 000 000 0.

I just wanted to show that there was little chance of 2 identical numbers being generated and that it had to be a caching issue. However, I didn't really grasp the tampa data issue.

Thanks Again and Best Wishes.

Adrian
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Phishing is at the top of most security top 10 efforts you should be pursuing in 2016 and beyond. If you don't have phishing incorporated into your Security Awareness Program yet, now is the time. Phishers, and the scams they use, are only going to …
Password hashing is better than message digests or encryption, and you should be using it instead of message digests or encryption.  Find out why and how in this article, which supplements the original article on PHP Client Registration, Login, Logo…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now