I am blulding a script that will show a small thumbnail from an original photo. In case the user paid for this image we will generate a link for this user to download it at anytime.
the problem is where is the best place to save the original photos? Also, how I can make sure that the user will not be able to access other original photos from the store?
What I was thinking of is to put the original photos in a folder let's call it orgImages/. When the user pay the download link will be like this.
http://www.mysite.com?download.php&token=sha1(UserLoggedInID)&pic=7726
in download.php I will write a smiler code
<?php
$user_logged_in_id = $_SESSION['current_user'];
$expected_token = sh1($user_logged_in_id);
if($_GET['token'] == $expected_token && isset($_GET['pic'])){
// get the picture original link
// generate .zip file that contains the image.
// let the user download the .zip file.
}
?>
I don't know if this the correct way to do it. I would feel better if someone's gave the correct way or a hint of how this process can be done.
It would work good if you add some secret key and the picture id, like:
$token = sha1('somesecretkey|' . $user_id . '|' . $picture_id);
In this case it's a bit more complicated to forge it.
Also it's easy to add an expiration date into this scheme if you ever want it:
$token = sha1('somesecretkey|' . $user_id . '|' . $picture_id . '|' . $expire);
and pass expire timestamp as a parameter. Then in script after you've checked that token is correct - check it's not expired yet.
Also, if you change your secret key - you could invalidate all the links at once.
Yes, you are on the correct way, you just have to make sure that you validate the $_GET["pic"] parameter before delivering anything to the user.
If this is not done right, you can open a giant security flaw on your software and server.
Related
Suppose a website xyz.com is showing ads from my ad network example.com using JavaScript code:
<script type='text/javascript' src='http://example.com/click.php?id=12345678'></script>
Which shows the ad as:
click.php
<a href="http://example.com/process.php?var1=var1&var2=var2">
<img src="http://example.com/ads/banner.png"/></a>
When the link is clicked it is taken to process.php where I add and subtract balance using some MySQL queries and then redirect to ad's URL.
process.php
$ua = $_SERVER['HTTP_USER_AGENT'];
$ip = $_SERVER['REMOTE_ADDR'];
//invalid click
if($_SERVER['HTTP_REFERER']==null || $_SERVER['HTTP_USER_AGENT']==null) {
header("location: http://example.com");
exit;
}
I want to add to an Unique Session at click.php and retrieve it at process.php to prevent invalid clicks. How do I do that?
Update:
The answer below solves half of the issue but the users are still able to send fake clicks using iframe and img tag as below:
<img src="http://example.com/click.php?id=12345678" height="1px" width="1px"/>
These clicks are still being counted as the request are served by both the pages click.php and process.php
What's the solution for this?
I have got a solution to the problem and it works perfectly:
EDIT:
I have found a solution:
To set the variables using sessions at click.php and sent it to process.php using a random number
click.php
$_SESSION["ip"]=$ip;
$_SESSION["ua"]=$ua;
$rand="".rand(1,9)."".rand(0,9)."".rand(0,9)."".rand(0,9)."".rand(0,9)."".rand(0,9)."";
$_SESSION["hash"]=$rand;
<a href="http://example.com/process.php?hash=$rand">
<img src="http://example.com/ads/banner.png"/></a>
and getting the values from the session at process.php
process.php
$hash=$_GET["hash"];
$ua=$_SESSION["ua"];
$ip=$_SESSION["ip"];
$rand=$_SESSION["hash"];
// Invalid Redirection Protection
if(($hash!=$rand) || ($ip!=$_SERVER['REMOTE_ADDR']) || ($ua!=$_SERVER['HTTP_USER_AGENT'])) {
header("location: http://example.com");
session_destroy();
exit;
}
If I have understood your question, your goal is to ensure that any requests arriving at http://example.com/process.php were from links created by http://example.com/click.php
(note that this only means that anyone trying to subvert your system needs to fetch http://example.com/click.php and extract the relevant data before fetching http://example.com/process.php. It raises the bar a little but it is a long way from being foolproof).
PHP already has a very good sessions mechanism. It would be easy to adapt to propogation via a url embedded in the script output (since you can't rely on cookies being available). However as it depends on writing to storage, its not very scalable.
I would go with a token with a finite number of predictable good states (and a much larger number of bad states). That means using some sort of encryption. While a symmetric cipher would give the easiest model to understand it's more tricky to implement than a hash based model.
With the hash model you would hash the values you are already sending with a secret salt and include the hash in the request. Then at the receiving end, repeat the exercise and compared the generated hash with the sent hash.
To prevent duplicate submissions you'd need to use some other identifier in the request vars - a large random number, the client IP address, the time....
define('SALT','4387trog83754kla');
function mk_protected_url($url)
{
$parts=parse_url($url);
$args=parse_str($parts['query']);
$args['timenow']=time();
$args['rand']=rand(1000,30000);
sort($args);
$q=http_build_query($args);
$args['hash']=sha1(SALT . $q);
$q=http_build_query($args);
return $parts['scheme'] . '://'
.$parts['host'] . '/'
.$parts['path'] . '?' . $q;
}
function chk_protected_url($url)
{
$parts=parse_url($url);
$args=parse_str($parts['query']);
$hash=$args['hash'];
unset($args['hash'];
// you might also want to validate other values in the query such as the age
$q=http_build_query($args);
$check=sha1(SALT . $q);
return ($hash === $check)
}
I am creating a website on Joomla and want to have a private page for each individual user. If anyone knows of the best way to do this apart from giving each individual their own group, this is the way I would like to try:
I want to create a page where php will match a file name based on the user id of the logged in user. This is the code I have so far:
<?php
$user = JFactory::getUser();
$user_id = $user->id;
$profilepage = file_get_contents ("user" + $user_id +"file.txt");
echo $profilepage;
?>
This gives me the user id in a variable, and I would like for the correct profilepage to show. For example, if User594 is logged in, then I want the file to be used for $profilepage to be "user" + 594 + "file.txt"...for an actual file name of user594file.txt. Is this possible? Or is there a correct way of doing this?
Thanks for any help!
Since you probably chose not to delete your question and not hearing from you in comment in regards to a comment to you, I decided to post my comment as an answer.
You're presently wanting to use + signs. This isn't the character to use in order to concatenate.
In PHP, you need to use dots/periods:
$profilepage = file_get_contents ("user" . $user_id ."file.txt");
How to securely authenticate a user without using any type of database.
authenticate.php?username={$_GET['username']}&password={$_GET['password']}
if ($_GET['username'] == "secret_username" && password == "secret_password")
{
$_SESSION['user'] = $username;
header("Location: password_protected_page.php");
exit;
}
This method seems to be an option. Is it secure?
Use a file to hold your data.
have a users.txt below your public html like so:
username:hashedpassword
then you use fopen
<?php
$filename = "/home/users.txt";
$file = fopen( $filename, "r" );
$display = fread( $file, filesize( $filename ) );
fclose($file);
?>
Then explode it by newline and then |, then check if the first is equal to username and the second is equal to md5(password).
Seems like the easiest way to me...
I would at least post for authentication but otherwise it should work fine.
Definitely, you can do that. But, just use POST.
There is nothing wrong with the process. Even when we use database, we actually do the same thing but just using some select command.
You might be thinking about password hash, but they are used so that, even if the 3rd party gets a hold of database dump(somehow), they can never actually decrypt the password, as hash are one way function. Now in you case you are not using database, so that's not a problem.
However the problem lies in scalability. Are you sure that there will always be just one user of the system. If yes, then its okay, else go for DB.
I learned PHP on my own. I never took a course nor had a mentor. I had issues "getting" datasbase calls, as they seemed so convoluted compared to other PHP which seemed natural. I started using this a long time ago.
You can create a text file (username.php) in a directory OFF the web server accesible folders.
(consider permissions!)
So you have /root/users and in that folder you have (by username)
/root/users.Joe.php
/root/users/Juan.php
/root/users/Tim.php
Tim.php contents
<?php
$userpath='/var/www/html/users/Tim';
$password='Timspassword';
?>
Now when Tim logs on wee have code that does this:
<?php
include '/root/users/'.$_POST[username].'.php';
if ($password == $_POST['password'])
{
$_SESSION['loggedin']='yes';
$_SESSION['expire']='<how much time you need?>';
}
?>
This way you can more easily create new users . BTW I use an index.php in each users folder that will do very little if not logged in as that particular user that matches the name of the folder. You should also use https. You could also use password encryption/decryption in these user passwords.
Truth be told, Database injection is a real vulberability. Daily I get hackers looking for databases on my sites. THERE ARE NONE, so they go away.
no databese required.
I have an app that uploads user files to S3. At the moment, the ACL for the folders and files is set to private.
I have created a db table (called docs) that stores the following info:
id
user_id
file_name (original file as specified by the user)
hash_name (random hash used to save the file on amazon)
So, when a user wants to download a file, I first check in the db table that they have access to file. I'd prefer to not have the file first downloaded to my server and then sent to the user - I'd like them to be able to grab the file directly from Amazon.
Is it OK to rely on a very very long hashname (making it basically impossible for anyone to randomly guess a filename)? In this case, I can set the ACL for each file to public-read.
Or, are there other options that I can use to serve the files whilst keeping them private?
Remember, once the link is out there, nothing prevents a user from sharing that link with others. Then again, nothing prevents the user from saving the file elsewhere and sharing a link to the copy of the file.
The best approach depends on your specific needs.
Option 1 - Time Limited Download URL
If applicable to your scenario, you can also create expiring (time-limited) custom links to the S3 contents. That would allow the user to download content for a limited amount of time, after which they would have to obtain a new link.
http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html
Option 2 - Obfuscated URL
If you value avoiding running the file through your web server over the risk that a URL, however obscure, might be intentionally shared, then use the hard-to-guess link name. This would allow a link to remain valid "forever", which means the link can be shared "forever".
Option 3 - Download through your server
If you are concerned about the link being shared and certainly want users to authenticate through your website, then serve the content through your website after verifying user credentials.
This option also allows the link to remain valid "forever" but require the user to log in (or perhaps just have an authentication cookie in the browser) to access the link.
I just want to post the PHP solution with code, if anybody has the same problem.
Here's the code I used:
$aws_access_key_id = 'AKIAIOSFODNN7EXAMPLE';
$aws_secret_key = 'YourSecretKey12345';
$aws_bucket = 'bucket';
$file_path = 'directory/image.jpg';
$timeout = '+10 minutes';
// get the URL!
$url = get_public_url($aws_access_key_id,$aws_secret_key,$aws_bucket,$file_path,$timeout);
// print the URL!
echo($url);
function get_public_url($keyID, $s3Key, $bucket, $filepath, $timeout)
{
$expires = strtotime($timeout);
$stringToSign = "GET\n\n\n{$expires}\n/{$aws_bucket}/{$file_path}";
$signature = urlencode(hex2b64(hmacsha1($s3Key, utf8_encode($stringToSign))));
$url = "https://{$bucket}.s3.amazonaws.com/{$file_path}?AWSAccessKeyId={$keyID}&Signature={$signature}&Expires={$expires}";
return $url;
}
function hmacsha1($key,$data)
{
$blocksize=64;
$hashfunc='sha1';
if (strlen($key)>$blocksize)
$key=pack('H*', $hashfunc($key));
$key=str_pad($key,$blocksize,chr(0x00));
$ipad=str_repeat(chr(0x36),$blocksize);
$opad=str_repeat(chr(0x5c),$blocksize);
$hmac = pack(
'H*',$hashfunc(
($key^$opad).pack(
'H*',$hashfunc(
($key^$ipad).$data
)
)
)
);
return bin2hex($hmac);
}
function hex2b64($str)
{
$raw = '';
for ($i=0; $i < strlen($str); $i+=2)
{
$raw .= chr(hexdec(substr($str, $i, 2)));
}
return base64_encode($raw);
}
I'm toying with the idea of creating automatic electronic certificates. It's pretty easy to create custom certificates using the fpdf PHP class. The way I have it set up is that given a URL
http://www.example.com/makepdf.php?name=myname&class=classname
you get a PDF certificate with the student name and the class they took taken from the $_GET variable. Of course, this means that anyone could manipulate the URL to very easily create a custom certificate. (They could do this in Photoshop anyway, but the idea is to make manipulating the certificate not totally trivial.) Once a class is over, I want to send a mail merge to everyone with a unique URL for their certificate.
How would you approach this problem? Should I just create a set of random numbers and associate these with the student/workshop pairs in a database? Are there standard ways of approaching this problem?
Couple solutions stand out:
Store the names & classes in a database, and reference them with a numeric ID instead of passing the data in the request
Keep the information in the request, but add a secure hash that will prevent tampering with the data
The hash mechanism would be something like this:
When generating the link for the certificate, you have $name and $class. You'll create a third GET variable that is a hash of $name, $class, and a secret string that only your program knows. Something like this:
$salt = "this is my secret";
$hash = md5($name . $class . $salt);
$url = "http://www.mysite.com/certificate.php?name=" . urlencode($name) . "&class=" . urlencode($class) . "&hash=" . $hash;
Now when a user hits your certificate generation page, you must verify the hash:
$salt = "this is my secret";
$expected = md5($_GET['name'] . $_GET['class'] . $salt);
if ($expected != $_GET['hash']) {
die("You are not authorized");
} else {
// User is OK; generate the certificate
}
Yes, if you want to limit your inputs to a fixed pool, then creating a database full of random keys is the way I would go.
If you want a quicker and dirtier way to do it, just generate the keys into a text file, use a script to pull the file apart to send them to the recipients, and have your PHP certificate generator read from a copy of the file on the server.
Assuming you are generating these URLs yourself on the server, you could join all your parameter values together into a string:
hash_string = "myname:classname";
Then append a final parameter that's a hash of that string along with some secret seed:
query_string .= "&h=" . md5("my_secret_key:" . hash_string)
Then, when you get the query back, just check to make sure that the hash matches:
hash_string = params['name'] . ':' . params['class'];
if (params['h'] == md5("my_secret_key:" . hash_string)) ...
I don't really know PHP syntax, but you get the idea.
Your best bet would be to have a list of students/classes (some kind of database) and only allow generation of allowed certificates. That way you don't need to obfuscate the name of the student or class, because only valid certificates can be generated.
If that's too much to ask - you could generate a MD5 hash based on the combination and some salt, then add that hash to the URL. That way the salt would need to be know to forge a URL.
http://www.example.com/makepdf.php?name=Tim&class=PHP&hash=c2c455ce438112b44499561131321126
Then the generation script just does this:
$hash = md5($_GET['name'] . $_GET['class'] . $salt);
if($hash != $_GET['hash']){
//invalid request
}
Of course you'll need to generate the URL's with the same salt.
Should I just create a set of random numbers and associate these with the student/workshop pairs in a database?
Yes.