Securely serving files from Amazon S3 - php

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);
}

Related

laravel rest api upload file to server

Am creating a REST api with laravel that allows a user to select an image file from their android device, then upload it to the server. The mage is converted to base64 before it's sent to the server alongside other parameters. I want to convert this base64 to a file and store it on the server then generate a link that can be used to access it. here is what i have tried so far and it doesnt work: I have already created a symlink to storage
public function create(Request $request)
{
$location = new Location();
$location->name = $request->location_name;
$location->latitude = $request->latitude;
$location->longitude = $request->longitude;
$location->saveOrFail();
$provider = new Provider();
$provider->name = $request->provider_name;
$provider->location_id = $location->id;
$provider->category_id = $request->category_id;
$provider->description = $request->description;
$provider->image = request()->file(base64_decode($request->encoded_image))->store('public/uploads');
$provider->saveOrFail();
return json_encode(array('status'=>'success', 'message'=>'Provider created successfully'));
}
As already commented by Amando, you can use the Intervention/Image package, having used it for many years I can say it will do what you want and very well.
What I would also add though, is you may also want to consider, whether you indeed need to store it as a file at all.
Depending on what it will be used for, and the size etc, you could consider storing it in the DB itself, along with any other information. This removes the dependency on a file server, and will make your application much more flexible with regards to infrastructure requirements.
At the end of the day, files are just data, if you will always get the file when you get the other data, reduce the steps and keep related data together.
Either way, hope you get it sorted :)

Download or Access to SharePoint Online Documents using PHP / XML / SOAP

I am working on a web project that involves connecting to SharePoint Online via PHP and accessing the files stored on it. But I am extremely new to all this, and have hit a wall.
I have the URL of the file I'm trying to access
Using the phpSPO library, I am authenticated and connected to SharePoint.
The question is: how do I actually access the URL? If I follow the link directly, it redirects me to the login page for SharePoint. But we want the login to happen "behind the scenes" - and apparently the authentication step doesn't quite do that.
The company we are working with told us that we would need to request an anonymous link for the URL by calling a function. Problem is, the function they told us to use works in ASPX, but doesn't appear to be available in PHP.
This is the code they pointed us to:
Uri siteUri = new Uri(siteUrl);
Web web = context.Web;
SecureString passWord = new Secure String();
foreach (char c in "password".ToCharArray())
passWord.AppendChar(c);
context.Credentials = new SharePointOnlineCredentials("userid", passWord);
WebDocs.Parameter1 = "123456"
WebDocs.Parameter2 = "Test"
context.Web.CreateAnonymousLinkForDocument(WebDocs.Parameter1, WebDocs.Parameter2, ExternalSharingDocumentOption.View);
But how can I translate that into PHP? Can I even do that?
And if not, is there another way that I can access the file to display it to my user?
// this says the function CreateAnonymousLinkForDocument doesn't exist
function getLink(ClientContext $ctx) {
$anonymousLink = $ctx->getWeb()->CreateAnonymousLinkForDocument();
$ctx->load($anonymousLink);
$ctx->executeQuery();
}
Well, after hours and hours of searching the Internet....
The answer was right in front of my nose.
Started browsing through the examples/SharePoint/file_examples.php that came with the phpSPO library, and discovered 2 functions (either one works).
One is called downloadFile, and the other is downloadFileAsStream.
function downloadFile(ClientRuntimeContext $ctx, $fileUrl, $targetFilePath){
$fileContent =
Office365\PHP\Client\SharePoint\File::openBinary($ctx,$fileUrl);
file_put_contents($targetFilePath, $fileContent);
print "File {$fileUrl} has been downloaded successfully\r\n";
}
function downloadFileAsStream(ClientRuntimeContext $ctx, $fileUrl,
$targetFilePath) {
$fileUrl = rawurlencode($fileUrl);
$fp = fopen($targetFilePath, 'w+');
$url = $ctx->getServiceRootUrl() . "web/getfilebyserverrelativeurl('$fileUrl')/\$value";
$options = new \Office365\PHP\Client\Runtime\Utilities\RequestOptions($url);
$options->StreamHandle = $fp;
$ctx->executeQueryDirect($options);
fclose($fp);
print "File {$fileUrl} has been downloaded successfully\r\n";
}
Since I was trying to download a PDF, I just set these functions to create a PDF on our own server.... and it works beautifully!!!!!

image protection using php

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.

Open txt files from a directory, compare the values, and output the top 15 in PHP

I recently designed a referral game website for the fun of it.
There's a simple MySQL user system with a email verification. It's using the UserCake user management system.
On top of this i added a php page that the user could give to "victims" that when they visit it they get "infected" and can infect other users or "victims". This page uses GET to get the username from the url. I have a folder that when a user registers it creates a file with 4 digits and then the username. (ex; 0000Username.txt) All the numbers are the same, it's just so that if a user discovers the folder they won't be able to find the files. There is also a txt file in the same format with IPS in the name. (ex; 0000IPSUsername.txt) The file when visited gets the username from the url, then checks if the text file for that username exists. If the username is present in the url, and a valid user it opens the IPS file and adds the IP of the visitor, then opens the user text file, takes the value and adds one to it, and saves. At the end it makes the difference between saying "You are infected, Username has infected (amount) people." or just you have been infected.
Now to what i need!
I need to add a hi-scores to the website so people can compete to be the one with the most "infections".
I thought i could use readdir to get a list of the files and open them with the value in an array, but i need it to also strip the username from the file name. It would be best if it just saves to a text file like "Username | value" because then i can add echo's of the html tags and have it include the file in the page i want it to be one.
Many thanks in advance.
Try using an array and count($array) to count the files.
Stripping the values out is as simple as using PHP's str_replace function. More here: http://php.net/manual/en/function.str-replace.php
With the 0000Username.txt file try using json.
// Get
$contents = file_get_contents('0000Username.txt');
$data = json_decode($contents); // PHP > 5.2.0
// Put
$data['username'] = 'Username';
$data['infectious'] = $data['infectious'] + 1;
$contents = json_encode($data); // PHP > 5.2.0
file_put_contents($contents);

create visitor unique ID?

I plan to create visitor unique ID and named as log file, as existing now I use the IP visitor as log file name i.e. logs/127.0.0.1.php but I think this is not enough because some visitor using share an IP address for PC's.
The visitor log file itself as setting place of configuration of visitors itself, so I plan to add another unique ID to identify each different visitor so let's say the log file:
logs/127.0.0.0.1-t3451dq.php, -t3451dq as unique ID
so as long as visitor browsing on my website the unique log file as setting configuration for each user (because I use plain text)
Currently I use:
<?
$filename = "./logs/".$_SERVER['REMOTE_ADDR'].".php" ; //out put logs/127.0.0.1.php
$data stripcslashes($data);
// each Visitor configuration here...
// bla...bla...
/* Writing file configurations */
$buat = fopen($filename, "w+");
fwrite($buat, "$data");
fclose($buat);
?>
so I need $filename add the $unique ID as name of their log file. Any ideas how to do that?
Try uniqid.
You can store this unique ID in the users session or in a cookie.
Example (not tested)
session_start();
if(!isset($_SESSION['uniqueID']))
{
$_SESSION['uniqueID'] = uniqid();
}
$filename = "./logs/".$_SESSION['uniqueID'].$_SERVER['REMOTE_ADDR'].".php" ;
Using a session will mean that if the same user closes their browser (or the session expires) they will get a new ID, which may or may not be what you want.
If you want a more persistent tracker then you may be better using cookies, and store the ID in the cookie (create a new ID if no cookie exists).
if(!isset($_COOKIE['uniqueID']))
{
$expire=time()+60*60*24*30;//however long you want
setcookie('uniqueID', uniqid(), $expire);
}
$filename = "./logs/".$_COOKIE['uniqueID'].$_SERVER['REMOTE_ADDR'].".php" ;
If you cannot use cookies/session then you may need to pass around the ID in your URL query string e.g. mypage.php?id=35dfgdfg3434
Create something out of his IP and the first time he enters the page. That should be unique.
You have two simple options : uniqid or as you're creating a file tempnam
Tempnam example :
function log($string, $userIP = null, $filename = null){
// Check if filename exists
if(!file_exists(LOG_PATH.$filename)){
$filename = tempname(LOG_PATH, $userIP.' - ');
if(!$filename){
return false;
}
}
// write log into file
$file = file_put_contents($filename, $string);
if($file === false || $file != strlen($string)){
return false;
}
return $filename
}
using log files for this type of use is unnecessary, it's alot easier to just shunt this type of data to a database. If it's just temporary data then use Cookies and/or Sessions

Categories