I am trying to make my website so that you have to log in in order to view some files. I got the login up and running with mySQL databases and everything is working fine, except that i don't want to manually put edit all my 1000+ html files to check if the user is logged in. I have tried using htaccess, but the popup is so ugly i can't stand it.
Now, the question is, can i password-protect a bunch of files on my website without manually modifying all of them, or can i make the "htaccess login form" look good.
Thanks.
You could put all of your HTML files in a directory outside of the webroot, then refer to them through URL rewriting or a basic querystring variable passed to a single PHP script.
For example:
<?php
// Get the file from ?whichfile=(...)
$whichfile = $_GET['whichfile'];
// Put your logic here to verify that the user is logged in / has a valid session ID, etc.
// You should also put some checks on the value that is passed through "whichfile"
// to prevent users from accessing things they shouldn't.
// Edit: example to prevent this:
// $whichfile = "../../../../etc/passwd";
$fname = pathinfo($whichfile, PATHINFO_FILENAME);
$ext = pathinfo($whichfile, PATHINFO_EXTENSION);
$fname .= ($ext ? ".".$ext : "");
if (file_exists("/var/www/folder/out/of/webroot/".$fname)) {
$blob = file_get_contents("/var/www/folder/out/of/webroot/".$fname);
header("Content-Type: text/html; charset=utf-8");
print $blob;
}
Related
I will completely clarify my question, sorry to everybody.
I have code writed in files from a website that now is not working, the html code is on pages with php extension, in a folder of a Virtual Host in my PC using Wampserever. C:\wamp\1VH\PRU1, when the site was online there was a folder where was a file called image.php. This file was called from other pages inside the site like this: (a little code of a file, C:\wamp\1VH\PRU1\example.php)
"<div><img src="https://www.example.com/img/image.php?f=images&folder=foods&type=salads&desc=green&dim=50&id=23" alt="green salad 23"></div>"
And the result was that the images was showed correctly.
Now, like i have this proyect in local, and i HAVE NOT the code of that image.php file i must to write it myself, this way the images will be showed the same way that when the site was online.
When the site was online and i open a image returned by that image.php file the URL was, following the example, https://example.com/images/foods/salads/green_50/23.png.
Now how the site is only local and i have not that image.php file writed because i'm bot sure how to write it, the images obviously are not showed.
In C:\wamp\1VH\PRU1\example.php the code of files was changed deleting "https://www.example.com/img/image.php?" for a local path "img/image.php?".
And in the same folder there is anothers: "img" folder (here must be allocated the image.php file), and "images" folder, inside it /foods/salads/green_50/23.png, 24.png.25.png..............
So i have exactly the same folder architecture that the online site and i changed the code that i could only, for example replacing with Jquery "https://www.example.com/img/image.php?" for "img/image.php?" but wich i can not do is replace all the code after the image.php file to obtain a image file.
So i think that the easiest way to can obtain the images normally is creating that IMAGE.PHP file that i have not here in my virtual host.
I'd like to know how to obtain the parameters and return the correct URL in the image,php file.
The image of the DIV EXAMPLE must be C:/wamp/1VH/PRU1/images/foods/salads/green_50/23.png
I have in my PC the correct folders and the images, i only need to write the image.php file.
Note that there are "&" and i must to unite the values of "desc=green&dim=50&" being the result: green_50 (a folder in my PC).
TVM.
You probably want something like this.
image.php
$id = intval($_GET['id']);
echo '<div><img src="images/foods/salads/green_50/'.$id.'.png" alt="green salad '.$id.'"></div>';
Then you would call this page
www.example.com/image.php?id=23
So you can see here in the url we have id=23 in the query part of the url. And we access this in PHP using $_GET['id']. Pretty simple. In this case it equals 23 if it was id=52 it would be that number instead.
Now the intval part is very important for security reasons you should never put user input directly into file paths. I won't get into the details of Directory Transversal attacks. But if you just allow anything in there that's what you would be vulnerable to. It's often overlooked, so you wouldn't be the first.
https://en.wikipedia.org/wiki/Directory_traversal_attack
Now granted the Server should have user permissions setup properly, but I say why gamble when we can be safe with 1 line of code.
This should get you started. For the rest of them I would setup a white list like this:
For
folder=foods
You would make an array with the permissible values,
$allowedFolders = [
'food',
'clothes'
'kids'
];
etc...
Then you would check it like this
///set a default
$folder = '';
if(!empty($_GET['folder'])){
if(in_array($_GET['folder'], $allowedFolders)){
$folder = $_GET['folder'].'/';
}else{
throw new Exception('Invalid value for "folder"');
}
}
etc...
Then at the end you would stitch all the "cleaned" values together. As I said before a lot of people simply neglect this and just put the stuff right in the path. But, it's not the right way to do it.
Anyway hope that helps.
You essentially just need to parse the $_GET parameters, then do a few checks that the file is found, a real image and then just serve the file by setting the appropriate content type header and then outputting the files contents.
This should do the trick:
<?php
// define expected GET parameters
$params = ['f', 'folder', 'type', 'desc', 'dim', 'id'];
// loop over parameters in order to build path: /imagenes/foods/salads/green_50/23.png
$path = null;
foreach ($params as $key => $param) {
if (isset($_GET[$param])) {
$path .= ($param == 'dim' ? '_' : '/').basename($_GET[$param]);
unset($params[$key]);
}
}
$path .= '.png';
// check all params were passed
if (!empty($params)) {
die('Invalid request');
}
// check file exists
if (!file_exists($path)) {
die('File does not exist');
}
// check file is image
if (!getimagesize($path)) {
die('Invalid image');
}
// all good serve file
header("Content-Type: image/png");
header('Content-Length: '.filesize($path));
readfile($path);
https://3v4l.org/tTALQ
use $_GET[];
<?php
$yourParam = $_GET['param_name'];
?>
I can obtain the values of parameters in the image.php file tis way:
<?php
$f = $_GET['f'];
$folder = $_GET['folder'];
$type = $_GET['type'];
$desc = $_GET['desc'];
$dim = $_GET['dim'];
$id = $_GET['id'];
?>
But what must i do for the image:
C:/wamp/1VH/PRU1/images/foods/salads/green_50/23.png
can be showed correctly in the DIV with IMG SRC atribute?
I face a case I never did, and I dont know how to properly do it.
I have a php script which generate files for clients. At the end of the script, I echo the path for them to download the file, simply.
How can I do to provide the file - or the path or any what - for downloading it, and be sure to delete the file once downloaded.
Widely, I'd like to make the file available for one/unique download only. How to ?
EDIT
I cannot use headers
There are a few components to getting this to work. Without knowing which framework you use, I'll use comments as placeholders.
There is no way to do it without using the header function, though.
Here is the source for a file that outlines the process:
<?php
$fileid = $_GET['fileid'];
$key = $_GET['key'];
// find the file in the database, and store it in $file
if ($keyMatches) {
// it is important for security to only use file paths from the database
$actualPath = $file->getPathOnDisk();
$fileInfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($fileInfo, $actualPath);
$fp = fopen($actualPath, 'rb');
header("Content-Type: " . $mime);
header("Content-Length: " . filesize($actualPath));
fpassthru($fp);
}
else
{
http_response_code(403); // forbidden
}
You'll use this by linking to download.php?fileid=1234&key=foobar, and generating the URL at the same time you generate the key and store it in the database.
For security, you'll keep the files outside of the web root, meaning they cannot be accessed through the web server without going through a script.
fpassthru is reasonably fast, and will not likely have a performance impact.
You must do a download file gateway, like download.php?id=XXX
Where XXX is the unique ID of each file you will store in DB. And of course, the file to be downloaded.
Then, each time a user will visit the page, you can :
- Check if he has already downloaded the file
- If no, redirect it to the real path of file
- If yes, display 403 message.
When a user download a file, update the DB, generate or copy the file to a new name, you play with headers, and delete file upon download or after a small timeout.
I am using TinyMCE as a WYSIWYG editor.
It is working perfectly, except for the image upload directory. I want each user to have their own directory in the images directory, but I cannot get it to work.
I am passing the user id in the URL and have tried adding the code to get it from the URL in the config.php file where the directories are defined, but the $user_id value remains empty.
Any assistance would be great.
The URL:
http://mydomain.co.za/index.php?user_id=1
The Code:
<?php
$user_id= htmlspecialchars($_GET["user_id"]);
// The URL that points to the upload folder on your site.
// Can be a relative or full URL (include the protocol and domain)
$imageURL = 'http://mydomain.co.za/images/'.$user_id;
// Full upload system path. Make sure you have write permissions to this folder
$uploadPath = '/home/username/public_html/editor/images/'.$user_id;
//We create the directory if it does not exist - you can remove this if you consider it a security risk
if(!is_dir($uploadPath)) {
mkdir($uploadPath,0755,true);
}
//Create thumb directory if doesn't exist
if(!is_dir($uploadPath . 'thumbnail')) {
mkdir($uploadPath . 'thumbnail',0755,true);
}
//Allowed extenstions
$allowedExtensions = array('jpg','gif','jpeg','bmp','tif','png');
//Maximum upload limit
$sizeLimit = 2 * 1024 * 1024;
function isAuth() {
//Perform your own authorization to make sure user is allowed to upload
return true;
}
Is it possible the reason is because it is not in the main php file?
Or Can I get the variable from the URL?
They suggested on their Instructions that I add $userId = Auth::getId(); but id returns an empty value. Plus I have no idea what that command is executing.
PLEASE NOTE:
the file management is being done by TinyMCE Image Uploader & Manager
UPDATE:
By adding the $actual_link = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"; echo $actual_link; I noticed by the time the $_GET command is rung the URL has changed to http://mydomain.co.za/tinymce/plugins/lioniteimages/connector/php/gallery.php, but in the browser URL bar, the URL is still the same with the variable.
Is there anyway to access that URL instead of the one i am getting?
I found the solution.
Simple enough, just created a session and the problem was solved.
I was able to get the variable from the session.
I want to protect a pdf file from being directly linked but instead have my logged in users be able to access it. I have a link which currently goes to a javascript function which posts a form:
$('nameofdoc').setProperty('value',doc);
document.getElementById('sendme').submit();
where sendme is the name of the form and nameof doc the index of the document I want to display.
This then goes to a php file:
$docpath = $holdingArray[0].$holdingArray[1];
$file = $holdingArray[0]; //file name
$filename = $holdingArray[1]; //path to the file]
header( 'Location:'.$docpath ) ;
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="'.$filename . '"');
readfile($filename)
This all works fine it loads up the file and outputs the pdf. What I can't do is protect the directory from direct linking - ie www.mydomain.com/pathToPdf/pdfname.pdf
I've thought of using .htaccess to protect the directory but it's on a shared host so I'm not sure about the security and anyway when I've tried I can't get it to work.
Any help would be great since this is my fourth day of trying to fix this.
thanks
Update
I've had a lot of help thank you but I'm not quite there yet.
I've got an .htaccess file that now launches another php file when a pdf is requested from the directory:
RewriteEngine on
RewriteRule ^(.*).(pdf)$ fileopen.php
When the fileopen.php file lauches it fails to open the pdf
$path = $_SERVER['REQUEST_URI'];
$paths = explode('/', $path);
$lastIndex = count($paths) - 1;
$fileName = $paths[$lastIndex];
$file = basename($path);
$filepath = $path;
if (file_exists($file)) {
header( 'Location: http://www.mydomain.com'.$path ) ;
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename=".$file);
readfile($filepath);
}else{
echo "file not found using path ".$path." and file is ".$file;
}
The output is
file not found using path /documents/6/Doc1.pdf and file is Doc1.pdf
but the file does exist and is in that direcotry - any ideas??
OKAY I'm happy to report that Jaroslav really helped me sort out the issue. His method works well but it is tricky to get all the directory stuff lined up. In the end I spent a few hours playing about with combinations to get it working but the principle he gave works well. Thanks
The best way would be to protect that folder with htaccess, as you have mentioned. So you put all PDFs in pdf/ folder, and in the same pdf folder you out .htaccess file:
RewriteEngine on
RewriteRule .* your-php-script.php
Now no files can be accessed by url in this folder. Every request to every file in this folder will return what your-php-script.php script returns. In your-php-script.php you do something like this:
//Check if user has right to access the file. If no, show access denied and exit the script.
$path = $_SERVER['REQUEST_URI'];
$paths = explode('/', path);
$lastIndex = count($paths) - 1;
$fileName = $paths[$lastIndex]; // Maybe add some code to detect subfolder if you have them
// Check if that file exists, if no show some error message
// Output headers here
readfile($filename);
Now if user opens domain.com/pdf/nsa-secrets.pdf Apache will run your-php-script.php. Script will have variable $_SERVER['REQUEST_URI'] set to "domain.com/pdf/nsa-secrets.pdf". You take the last part (filename) and output it to a user (or not).
This will stop anyone from accessing files directly from the internet by knowing URL. If someone has direct access to files on your server, that will not stop them. On the other hand, I think any shared hosting stops users from getting files of other clients. Only way to do it is to hack the server in some way. But then we are getting very paranoid and if that may be a case for you, you shouldn't use shared hosting in the first place.
If you cannot make htaccess work, you can try to obfuscate files, so it would be difficult to spot them for someone outside. For example change file from mySecretData.pdf to djjsdmdkjeksm.pdf. This may help a little bit.
I want to protect a pdf file from being directly linked but instead have my logged in users be able to access it.
Check to ensure there is an authenticated user before streaming the PDF's content.
This is kinda sloppy but it could work assuming you can setup a MYSQL DB. It lets you pass the "password" in the URL as an MD5 string or as a clear text if you want to. Trying to setup some kind of security without using htaccess or an existing frame work is kinda clunky. This however won't even attach the file to the stream until it knows you've been "Authenticated" I think you could maybe make this a little better if you setup a login page that saved a cookie locally then you wouldn't need to pass the "passphrase" in the URL.
$file = $_GET['file'];
$pass = $_GET['pass'];
$download_folder = '../Protected';
$file = basename($file);
$filepath = "$download_folder/$file";
if (file_exists($filepath)) {
if(CheckUser($pass)){
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=$file");
session_write_close();
readfile($filepath);
} else {
echo 'Not Authenticated!';
}
} else {
echo 'No File!';
}
function CheckUser($value){
$con = mysqli_connect("test.com","test","123456","my_db");
// Check connection
if (mysqli_connect_errno()){
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$result = mysqli_query($con,"SELECT user FROM pass_table WHERE password =".md5($value).";");
while($row = mysqli_fetch_array($result)){
mysqli_close($con);
//return $row['user'];
if($row['user']){
return true;
}
}
mysqli_close($con);
return false;
}
I wanna create a few unique download link for my users. The reason is that I wanted to let them download once only, so that they can use back the same link to download again.
I've generate a few of the keys (example, qwertyasdfghzxcbn. As in the download link will be like www.xxxxx.com/download.php?qwertyasdfghzxcbn) in the database and flag field where when the user downloaded, it will update 1 to the flag field.
I did a search on the net and found this.
http://www.webvamp.co.uk/blog/coding/creating-one-time-download-links/
But that only works when you go to the page first then only the page will generate the unique link. I've already pre-generate the link inside my database, I don't need to regenerate again, if fact if I generate the key when user go the page, they will able to download multiple times by refreshing the page.
The solution would be to make the link target itself a PHP script.
You'd hide the actual file somewhere inaccessible from the browser (i.e., somewhere where you can reach the file via fopen(), but isn't within the document root), and put a download.php file to download files.
The download script itself would look something like this:
$fileid = $_REQUEST['file'];
$file = file_location($fileid); // you'd write this function somehow
if ($file === null) die("The file doesn't exist");
$allowed = check_permissions_for($file, $fileid) // again, write this
// the previous line would allow you to implement arbitrary checks on the file
if ($allowed) {
mark_downloaded($fileid, $file); // so you mark it as downloaded if it's single-use
header("Content-Type: application/octet-stream"); // downloadable file
echo file_get_contents($file);
return 0; // running a return 0; from outside any function ends the script
} else
die("You're not allowed to download this file");
Any link you point would simply point to download.php?fileid=712984 (whatever the fileid actually is). That would be the actual download link, since that script does transfer the file; but only if the user is allowed to retrieve it. You'd have to write the file_location(), check_permissions_for() and mark_downloaded() functions yourself though.
I would suggest using uniqid() function, and store unique ids with the expiration date in a database, while returning to the user url with something like this: ...?file_id=$id
When the link is being opened, you may delete it from the database or mark it to be deleted 'soon' (just in case user wants to refresh the page.)