PHP file cache triggers a Internal 307 == insecure warning - php

In a PHP 7.1 app I load files from a php file "filecache.php" - it works fine in regards of returning a cachable file.
But the thing that drives me crazy is that the images triggers a Insecure content warning in Chrome and FF (perhaps others also).
The files are loaded using a tag, I tried relative url and this gives me the error. I've also tried using complete url https://example.com/filecache?f=0983490842'> same error.
The server uses HSTS and LetsEncrypt cert - no http traffic allowed.
When I examine the Network activity in Chrome (and FF) I can see that the browser tries to retrieve a file using https, finds it in the cache which gives a 307 internal redirect - but to a http url - finally ending up with the image loaded over https from the cache. Well at least thats how I read the info below.
Any input or pointers will be greatly appreciated!
filecache.php
if(file_exists($file)){
if(substr($_GET["f"],-3)=="jpg") Header("Content-Type: image/jpeg");
if(substr($_GET["f"],-3)=="png") header("Content-Type: image/png");
header('Cache-control: max-age='.(60*60*24*365));
header('Expires: '.date("Y-m-d H:i:s",strtotime("+365 days")));
header('Last-Modified: '.gmdate(DATE_RFC1123,filemtime($file)));
readfile($file);
}else{
die("no such file");
}

Related

Video streaming with php. Why is the response slow?

I have a big problem. I have a video streamer site. The site has public and private videos, private videos can be seen after purchase.
Here you can see the directory structure:
/public_html
|__/vendor
|__/src
|__/public
|__/product
|__/sample-video
|__/video.mp4
Well, as you can see the videos are located outside the public folder. The goal is to make them inaccessible directly.
Here is the twig:
<video id="myVideo">
<source src="/products/12?video=10&cache={{ random(50, 10000) }}" type="video/webm" />
<source src="/products/12?video=10&cache={{ random(50, 10000) }}" type="video/mp4" />
</video>
The video controller:
https://pastebin.com/sne1mSHH
And the video stream logic:
https://pastebin.com/2Sxd7Nqq
Here's the problem:
I'm going to the site. You switch between pages. Perfectly good. I go to a page with a video still good and after I start the video, the page is not good.. The page just loads and does nothing. I can't switch between pages anymore. I have to close the incognito mode and start a new session.
Until now I thought that the session lock was the problem, but I close the write session.
I tried opening the video in a new tab. Also the error is until the video stream is finished, the page does not handle the request.
So while the video is playing, while I can't send a new request.
What else can I attach to make the error more transparent?
Help pls!
I would personnally not use PHP to stream the video. The problem is that you'll have to many PHP processes locked for reading and streaming a big file instead of handling logic. You will also have a PHP timeout during this process.
Instead, I would use the Sendfile module:
You install an Apache module or other kind of Sendfile module for your web server, NGINX or whatever.
In PHP, you do the logic for the protection and just send a HTTP header to say you want Sendfile to handle the streaming. This way your PHP code stops running and its the web server that handles the transmission of the file.
Something like this:
<?php
if (has_access_to_the_video($file))
{
// Send the right HTTP headers.
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Content-type: ' . mime_content_type($file));
header('Content-Length: ' . filesize($file));
// Make sure you have X-Sendfile module installed on your server.
header('X-Sendfile: ' . $file);
exit;
}
else
{
// Generate your 403 error or whatever.
}
This may help you:
https://symfony.com/doc/current/components/http_foundation.html then search for sendfile in the page.
https://github.com/mostafaznv/php-x-sendfile
PHP File Serving using X-Sendfile
https://www.h3xed.com/programming/how-to-use-x-sendfile-with-php-apache
Another remark regarding your video sources
For the <video> tag, you are declaring two <source> elements, one with type="video/webm" and the other with type="video/mp4" but both point to the same URL. In your PHP logic, I don't see any handling of the desired content type so your server is probably returning the same video file for both codecs and this is problematic. I would add the video content type in the URL and on the server side, do the internal redirection with Sendfile to the correct file (MP4, OGG, WEBM, etc). If the browser requests a WEBM file and recieves a MP4 H.264 instead, I assume it will not load correctly.
It seems that MP4 H.264 is widely handled by know, so you could just stick with one source. See the current support here:
https://caniuse.com/mpeg4
https://caniuse.com/webm
https://caniuse.com/?search=ogg

PHP: filesize() stat failed with link

I'm randomly getting download errors from a link on a page. I also simplified the link to a directory for easy usage in emails for users.
On the main page the link looks like this:
a href="http://myPage.com/Mac" target="_blank" id="macDownloadButton" class="downloadbutton w-button">Download Mac version</a>
On my server, that's a directory with an index.php in it which looks like this:
<?php
// mac version
$file="http://www.myPage.com/downloads/myApp_Mac.zip";
$filename="myApp_Mac.zip";
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
header('Content-Length: ' . filesize($file));
header('Content-Encoding: none');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename=' . $filename);
readfile($file);
exit;
?>
Again, the reason I do this is so it's a simple link to send to users in email like, "http://myPage.com/Mac" and "http://myPage.com/Windows".
The weird thing is that it mostly works...but sometimes it doesn't.
What am I doing wrong?
It's hard to know precisely what's wrong unless you check for errors on your readfile() call.
But you're invoking your web server from your web server here when you specify a filename starting with http. You're doing
readfile('http://www.myPage.com/downloads/myApp_Mac.zip');
where you could just as easily do
readfile('../downloads/myApp_Mac.zip');
and read the zip file from the local file system to send to your user.
What's more, filesize('../downloads/myApp_Mac.zip'); will yield a numerical value quickly and send it in the Content-Length header. That will allow the browser, by knowing the total size of the file you're sending, to display a meaningful progress bar.
You should remove the Accept-Ranges header; the php program you showed us doesn't honor range requests. If you lie to the browser by telling it you do honor those requests, the browser may get confused and corrupt the downloaded copy of your file. That will baffle your user.
Your Content-Disposition header is perfect. It defines the filename to be used on your user's machine in the downloads folder.
Simple operations are more reliable, and this may help you.
The reason you got stat failed with link as an error message is this: stat(2) is a operating-system call that operates on files in local and mounted file systems.
As previously mentioned by O. Jones you should definitely always use your local file path.
Most of my previous issues have been mostly browser related where I needed to tweak/add a http header, and in one case I needed to send all the HTTP headers in lowercase but I haven't had an issue like that in years. My personal recommendation would be to use a solid download library/function - it will make a noticeable difference to your productivity as well as rule out most browser related issues you may come across.
I have used the codeIgniter download helper for the last 3 years and recommend it for 99% of use cases. At the very least I would recommend your read through it's code - you will probably find a few cases you have never even considered such as clearing the output buffer,mime detection and even a special case for Android 2.1 as well as a few headers you may or may not need.
If all else fails I have no idea what server your running this on but if you continue to have issues I would recommend monitoring which processes your machine is running while paying close attention to ram and IO usage. I've have encountered bad/misbehaving services that run periodically using 99% of my IO or ram for short intervals at a time that caused a few really odd and unexpected errors.

Download PDF - Failed: Network error, PHP-FPM and Nginx

I have a very simple script for downloading a pdf:
$path = __DIR__.'/sample_file.pdf';
$pathinfo = pathinfo($path);
$fsize = filesize($path);
$filename = $pathinfo['basename'];
header('Content-Type: application/pdf');
header("Pragma:public");
header("Content-Disposition:attachment;filename=" . $filename);
header('Content-Length: ' . $fsize);
header("Pragma: no-cache");
#readfile($path);
exit;
This works fine for files smaller than 10mb. But anything over that I get an error and the download fails. Have tried in a number of browsers and get similar results. In all cases the download fails.
Chrome: Failed: Network error
Firefox: [filename].pdf.part could not be saved, because the source file could not be read.
Opera: Interrupted: Network error
IE (10): [filename].pdf couldn't be downloaded.
I know the filepaths are correct, or else it wouldn't work with files smaller than 5mb.
Reading the php docs (http://php.net/manual/en/function.readfile.php) on readfile some have suggested disabling output_buffering (currently set to 4096 in php.ini) before calling readfile. I have yet to try this but I'm not convinced it is the solution.
zlib.output_compression is disabled. I am not seeing any errors in my logs. I am also not seeing any errors in the network pane in Chrome inspector.
I have tried downloading with chunks but get the same result. I have researched similar answers but all seem to be browser specific, i.e. working in some browsers but not others.
Maybe you use pausing or something like this and you target file is locking. It is possible that your security software is scanning and locking the file, which may still be in the system temporary folder after you pause the download.

Caching a GET request for audio with PHP

I have a PHP script that responds to a GET request for audio resources. An HTML5 Audio tag requests an audio file such as:
<audio src="get_audio.php?f=fun" preload></audio>
There is no need for the user to download that same audio file every time so I would like to cache it. In my PHP file I have:
header("Cache-Control: max-age=2419200");
header("Content-Type: audio/mpeg");
...
echo file_get_contents($path);
but when I view the Network tab of Chrome developer tools I see that it re-downloads the audio clip everytime rather than saying "from cache" and if I look in the Response headers I do see the Cache-Control header that I set. Why would it ignore this? Amidoingitright?
It's been a while since I did this in PHP, but try adding:
header("Pragma: public");
above the cache-control header.
I also think you need the expires header:
header('Expires: ' . gmdate('D, d M Y H:i:s', time()+2419200) . ' GMT');
Other than that you could start using the get_headers() function in PHP to debug where it's going wrong.
Do you see a header in the second request called If-Modified-Since:?
This is what you need to catch, parse and respond to - if you don't want to send the file again, you respond with HTTP/1.1 304 Not Modified. If you are using Apache, you can check for the header in the result of apache_request_headers(). If you are not using Apache, you may find it hard to handle this - you will probably have to find a way for the web server to set the headers as environment variables, so they are available in $_ENV or $_SERVER. There is a way to do this in Apache using mod_rewrite (see latest comment on page linked above), so there is probably a way to do it in other server environments as well.
Caching in HTTP 1.1 permits (and indeed, encourages) this behaviour - it means that the cached copy will always be up-to-date.

php: force download from different domains?

i wonder if you guys come up with some awesome solutions for my problem. the normal way is not working!
Well, i'm trying to force-download any file on any website with the following php script. I just pass ?p=http://www.whatever.com/images/flowers/rose.jpg to my url and the download prompts.
<?php
error_reporting(E_ALL);
if(isset($_GET['p'])) $path = $_GET['p'];
else echo "no path set!";
$file = $path;
//header('Location:' . $file); //works perfect, opens the file in the browser
//
header("Cache-Control: no-cache");
header("Expires: -1");
header("Content-Type: application/octet-stream;");
header("Content-Disposition: attachment; filename=\"" . basename($file) . "\";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($file));
echo readfile($file);
?>
However, as I found out today filesize() just works with local files on my server not with an http request. The same applies to readfile()
Warning: filesize() [function.filesize]: stat failed for
pathtofiles…/downloader/d.php on line
15
Warning:
readfile(sphotos.ak.fbcdn.net/hphotos-ak-snc4/hs211.snc4/…)
[function.readfile]: failed to open
stream: HTTP request failed! HTTP/1.0
403 Forbidden in
pathtofiles…/downloader/d.php on line
16
i wonder if there are creative coders out there who can help me out here. Is there any chance for me to make that script work? I just want to forcedownload whatever file and url you pass along.
thank you advance,
regards matt
filesize() and readfile() can work with some protocols if your PHP settings allow.
The problem at hand is a more fundamental one, though:
HTTP request failed! HTTP/1.0 403 Forbidden in pathtofiles....
remember that when fetching a file using PHP, it does not have the user's login permissions available. The script instaince is acting like an independent browser. If a resource is password protected, you need to make your PHP script log in first.
You cannot 'force-download' for a file that is NOT under your control (as a remote file). The 'force-download' tells the browser to download the file that is about to be transmitted. Location: some-path tells the browser to look for the new location, thus listening the new location, not your current page.
One option, but not optimal, would be to make a local copy of the file (at your server), then retrieve it to the user. If the file is big enough, this will give the impression of a frozen page. To avoid this, you can read chunks of the remote file, and deliver after each read command.
Be aware, your code does not restrict what $file can download, thus allowing users to download virtually any readable file at the server, this is a security flaw.
The best way is to store filesize in your database if you have control over remote files and then use readfile function.
From PHP manual
readfile() You can use a URL as a filename with this function if the fopen wrappers have been enabled.

Categories