chriscct7
chriscct7

Reputation: 527

PHP file downloads on direct visit

I am very much aware that PHP is a server side language and therefore should not allow the php file to be downloaded. However, on direct visit in Chrome to the PHP file, it downloads an obfuscated version of the file, something I'd like to prevent. Is there a way to stop the file from being served up?

The code is below.
The system works to perfection inside WordPress, but if I open up Chrome (and I presume others) and visit the update.php file directly, it downloads.

Notably, I tried to echo an HTML page but it messes up the way the system works. I'm hoping there's some sort of .htaccess trick for this.

if (isset($_POST['action'])) {
  switch ($_POST['action']) {
    case 'version':
      echo '1.1';
      break;
    case 'info':
      $obj = new stdClass();
      $obj->slug = 'plugin.php';
      $obj->plugin_name = 'plugin.php';
      $obj->new_version = '1.1';
      $obj->requires = '3.0';
      $obj->tested = '3.3.1';
      $obj->downloaded = 12540;
      $obj->last_updated = '2012-01-12';
      $obj->sections = array(
        'description' => 'The new version of the Auto-Update plugin',
        'another_section' => 'This is another section',
        'changelog' => 'Some new features'
      );
      $obj->download_link = 'http://localhost/update.php';
      echo serialize($obj);
    case 'license':
      echo 'false';
      break;
  }
} else {
    header('Cache-Control: public');
    header('Content-Description: File Transfer');
    header('Content-Type: application/zip');
    readfile('update.zip');
}

Upvotes: 0

Views: 746

Answers (2)

Mike S.
Mike S.

Reputation: 4879

You set the content type to application/zip which in most browsers prompts a download. Is there a chance that you're not passing an "action" via POST and that is why it's hitting that else section?

One more thing I see is you have no

break;

In your second switch/case.

      $obj->download_link = 'http://localhost/update.php';
      echo serialize($obj);
      break;  // <------ this is missing!
    case 'license':
      echo 'false';
      break;

Have you checked out this article: http://konstruktors.com/blog/wordpress/2538-automatic-updates-for-plugins-and-themes-hosted-outside-wordpress-extend/


Regarding restricting access to the file to everyone except WordPress, it's unknown if you host your WordPress site or not. If you do, you could restrict access to localhost or 127.0.0.1 in your .htaccess file. If it's hosted on WordPress.com you find their IP or hostname and change it.

<Files update.php>
    Order allow,deny
    Deny from all
    Allow from 127.0.0.1
</Files>

Upvotes: 0

Michael Berkowski
Michael Berkowski

Reputation: 270607

Your code, if it does not receive $_POST['action'], then sends update.zip to the browser in the else case.

What you are seeing when visit the file not via a POST is not obfuscated PHP. Rather, it is sending the contents of the file update.zip to the browser for download. But since the code doesn't supply a filename hint in the headers, it doesn't come as update.zip and instead probably looks like a .php file with the same name as your script.

If you want update.zip to look like a zip file, add a filename into the Content-Disposition output header:

header('Cache-Control: public');
header('Content-Description: File Transfer');

// Change to attachment disposition, with filename
header('Content-Disposition: attachment; filename=update.zip');
header('Content-Type: application/zip');
readfile('update.zip');

Now, if you don't want it sending update.zip at all, remove the entire else {} block from the bottom, and replace it with something like

else {
  echo "You must supply an action...";
}

Update: To restrict access only to referrals by wp_autoupdate.php

Consult $_SERVER['HTTP_REFERRER'], but know that the value of this can be spoofed. This cannot be used with 100% reliability.

if (strpos($_SERVER['HTTP_REFERER'], 'wp_autoupdate.php') !== FALSE) {
  // Include all your exsiting code
}
else {
  // Don't do anything, or redirect somewhere else
  header("Location: /");
  exit();
}

To achieve 100% reliability, you would probably need to modify wp_autoupdate.php to set a session variable which is then checked by update.php, ensuring the request came from the right place.

Upvotes: 1

Related Questions