tags:

views:

727

answers:

3

I'm running a cronjob which needs to write data to a cache. I wanted to put this cronjob in my private folder, however, even after CHMODding the cache folder in the private folder, it's not getting writing rights. This seems to be somekind of plesk feature.

So, now I've placed the cronjob in the public folder. However, I need to make sure that only the server can execute the script. What about the following at the top of the cronjob?

if ($_SERVER['SERVER_ADDR'] != $_SERVER['REMOTE_ADDR']) die();

This seems to work. Is it not exploitable however, eg. can a user manipulate his remote_addr to my server's? Or is there a better way to check this?

Another issue I have is that the above code is returning 2 warnings, even though it does seem to work:

PHP Notice:  Undefined index:  SERVER_ADDR in ... on line 2
PHP Notice:  Undefined index:  REMOTE_ADDR in ... on line 2

Any idea what's the cause of that?

+4  A: 

First, I would keep the executable PHP script in a private directory that can only be executed by the privileged user - and schedule it in the cron for that user. You should have no problems executing the script.

Second, you can change where you are writing the cache data to a public directory. It would be preferable to keep this in a non-public directory, but if you cannot figure out how to do it otherwise, and the data is okay to be public, then it is probably okay.

Finally, using $_SERVER['SERVER_ADDR'] and $_SERVER['REMOTE_ADDR'] is probably not the best way to check to see if the server is running the script. For one, I believe that $_SERVER['REMOTE_ADDR'] will not be set when running a command-line PHP script. This is taken from the HTTP headers, and since there are none, it will be empty. In fact, this is why you are getting the warnings - these are not set. (edit: looking at the PHP docs, it is likely that the 'SERVER_ADDR' variable will not be set for non-browser scripts)

jonstjohn
how would I create such directories on a shared webhost?
Tom
+4  A: 

Execute the script via the console, not the web server.

The cron could look like this:

*/5 * * * * php -f /path/to/cron.php

Then the file could do this:

cron.php:
<?php

if ( array_key_exists('REMOTE_ADDR', $_SERVER) )
    die("Can only be run from the command line!");

That will guarantee it's only run by the server.

Edit in response to comments

You can't get to a public folder inside a private folder, in general, and if you can't add new directories outside the web root, your cache dir will have to be protected another way.

I'm going to assume all your files are in the web root, ie: /home/site/publichtml. Replace that with whatever your directory is.

Create a new directory /home/site/publichtml/.cache. Add the following as a .htaccess file:

Deny from all

Now your scripts should be able to access the folder from the file system, but it's inaccessible via the web server.

If you can set up a cron job on the server (via the web admin or another way, it sounds like you can) do it as above, ie: Use php -f /home/site/publichtml/cron.php as the command, and include the check for the array key.

Alternatively, you can check like this:

if ( !isset($_SERVER['argc']) )
    die("Must be run from the command line!\n");

$_SERVER['argc'] is only set when the script is executed from the command line.

If you can keep the cron script out of the web root, good. If not, that should be secure.

Finally, to get rid of the E_NOTICES, add this to the top of the cron.php:

error_reporting(E_ALL ^ E_NOTICE); // Turn off notices

or

error_reporting(0); // Hide all error reporting
James Socol
I don't have access to the console unfortunately. It's a shared webhost.
Tom
If you can set up cron jobs (via an admin panel) you should still be able to make this work, but you might lose some of the security (eg: need to move the cache dir into the web root).
James Socol
I tried creating a new folder before document root in my ftp client, however I get a permission denied message.
Tom
Should talk to your host, maybe shell access is an option they just don't enable by default. I do that for most of my clients. If your in a plesk environment, the security is so locked down, they should have little concern.
Adam
+1  A: 

I think the answer is hidden in your first reply from jon.

So if you only want this script accessed via cron, check for the existence of the session superglobals:

if (is_array($_SESSION)) die();

And, just because it's easy, create a .htaccess file and do an deny-all.

I typo'd the $_SESSION as $_SERVER, my bad. That works for your needs.

Encoderer
This isn't true. Run this script from the command line: "print_r($_SERVER);".
James Socol
Just found that one out, doesn't work Encoderer.
Tom
There was a typo. Fixed now.
Encoderer