How safe is this?
Realistically, it's pretty safe as long as you're in control of the address (127.0.0.1 is okay, XXX.XXX.XXX.XXX might not be). By pretty safe I mean that there's little chance that someone might abuse this system and not have a far greater chance of abusing the rest of the web application.
What are the risks?
Someone might call your script from outside, if they had a way of assuming the IP address XXX.XXX.XXX.XXX in some way, or tricking the systemm into believing they had.
How can I make this more safe?
You can include a secret in the original call, and check it against a hash of the same secret. The secret is not revealed even if someone can read the script.
if (!array_key_exists('key', $_GET)) {
die('Access denied');
}
if (sha1($_GET['key']) !== '713dca7cf928f23a2347cae828d98879629e1e80') {
die('Access denied');
}
You can also place the script outside the web root, and call it through a require
statement. This way, either the PHP subsystem works, and the script cannot be read, or it does not work, and all that's revealed is the name of an unaccessible directory. You can even merge the two approaches:
if (sha1($_GET['key']) !== '713dca7cf928f23a2347cae828d98879629e1e80') {
die('Access denied');
}
$realScript = $_GET['key'];
require $realScript;
Now, the only script that can be included is the one whose name has that specific SHA1 hash, and no other (the risk of collisions is realistically negligible: you would need a collision with a valid filename, and the means of creating such a filename). So you know that the script is valid, but unless the name is supplied in the call, the whole construction will not work, and it will not even tell an attacker why.
curl http://yoursite.internal.address/cron/cron.php?key=../scripts7ab9ceef/mycron.php
Are there other (better) solutions?
Yes and no. Calling the script using the command line interface is just as safe, and does not need a working webserver. It also allows running as a different user if needed.
On the other hand, it requires a command-line interface to be installed, which may create other security issues, and even if the same user and home directory is used, the two interfaces might still behave subtly differently (or not so subtly: you might have a PHP7.3 web module and a PHP5.2 CLI installation, or vice versa, which would make a script with short array syntax (or with constructs like if(empty(some_function($_GET['x']))
) not even load in one or the other interface.
All in all, the crontab call to curl
or lynx
is probably more maintainable and simple to use, even if it is undoubtedly less efficient.