I am running a PHP script via linux cronjob and I want to make sure that it can be run remotely only from the computer whose ip address I specify, plus via cronjob.
Now, I can check the remote ip addresses from $_SERVER['REMOTE_ADDR'], but doing so would also stop execution via cronjob.So, how to make both things work?
You'll need to check if it is run from the command-line too to handle the cron case
if (php_sapi_name() == 'cli' || $_SERVER['REMOTE_ADDR'] == 'your.ip.add.ress') {
// allow
}
Put the cronjob out of your web root.
Then you can check wheather the cron is running over a cli:
if (php_sapi_name() != 'cli') {
die();
}
Its no good idea to tun your cron over your webserver. Then every people can start it.
You can use php_sapi_name function to check for local (cron) execution in addition to checking IP addresses, something like this:
if (php_sapi_name() == 'cli' || $_SERVER['REMOTE_ADDR'] == 'xxx.yyy.zzz.vvv') {
//do your stuff
}
else {
/show some error
}
That said, you need to remember that remote address can be easily spoofed, therefore it's not good to rely on it, at least if the server is open to the internet. It's a bit more secure if you're running on a local network.
Related
I have a UBNT Toughswitch and am trying to remotely turn off poe power with my cell phone with php and scripts. I do this with similar scripts with UBNT mFi. The script works fine if I SSH into the server (w/ root, only user I ever setup) and run it in the location its in. I checked all the R/W/E boxes on the script and php code in case it was a php permission issue. So here is the scripts:
PHP to invoke the script with a http get request:
<?php
system ( "./tsport3-24-off.sh" );
?>
<script type="text/javascript">
window.close();
</script>
Here is the Expect Script:
#!/usr/bin/expect
set timeout 30
spawn ssh shane#$ip
expect {
"*assword:*" {
send "$password\r"
expect {
"*assword:*" {
close
continue
}
"*#*" {
send "grep -v 'switch.port.3.poe=48' /var/tmp/system.cfg > /var/tmp/tmpsystem.cfg; mv /var/tmp/tmpsystem.cfg /var/tmp/system.cfg\r"
expect {
"*not found*" {
close
}
"*#*" {
send "save && reboot\r"
interact
}
}
}
}
}
}
It will get through everything until the save && reboot portion and won't do that when invoked via php. Through SSH, it works fine. I also tried save and reboot with 2 different commands in case it didn't like &&, and isolated that into a separate script to make sure that is where its at. It's like php just refuses to send save && reboot for some reason like a permission problem, but it's sending it to a different device trough ssh so it doesn't make sense to me. This one is to turn off poe 24v on port 3. I have others for on (24/48) and reboot that all work without php, but not with php.
Not a 100% answer, but it works.
If I add another save && reboot like so:
expect "*#*"
send "save && reboot/r"
expect "*#*"
send "save && reboot/r"
It doesn't error out when run by the php script, probably does if I run it normal, but that was never my intention anyway. Think it's a timing issue or it's picking up an extra # somewhere when being run by php.
This is the code I'm using, is it enough to make sure the script can be only run as a cron job? It sure does work but maybe there is something I have missed.
if (php_sapi_name() !== 'cli') {
die("You are not allwod here");
}
Yes, that will work. I would also make sure the script is outside the root directory.
This could be a kind of strange question. I have a php file in a public web site where I have access to the server. For the moment we do not need to set Cron job for this file execution. However I need to limit the execution of this file only via localhost. I don't have any precise idea about the way should I do this. Is there any way to detect whether the request is localhost or not? I mean with PHP. Or should I need to handle this via security setting or firewall of the server?
I'm assuming you mean you want to be able to execute the script only from the server, and not via the web from another host. You have two options:
Option 1
move it out of the public_html folder. There is no reason you should not take this option, unless there is something preventing you from doing so. In that case,
Option 2:
Wrap the entire code in the following if statement
if (php_sapi_name() == 'cli')
{
//your code
}
Alternatively,
if (php_sapi_name() != 'cli')
die();
//your code
This ensures your script will only run if invoked from the command line, and not via the web.
You could check IP addresses like this:
if($_SERVER["REMOTE_ADDR"]!=$_SERVER["SERVER_ADDR"])
{
exit;
}
I know how to run a script with a cron, but what I need is to be able to run my script only by a cron.
Thank you!
You should keep this script outside of the public folder. Also, set appropriate permissions to the file so public users can not execute the script.
Put below code snippet to the top of your script.
if(php_sapi_name() !== 'cli'){
die('Can only be executed via CLI');
}
Note that you need to use the full path to the PHP executable when you setup your cron job.
Ex : /usr/local/bin/php (Your path may be differ from this)
As explained in this duplicate thread:
PHP & cron: security issues
You should keep this file outside of public_html.
Sometimes, though, this is not possible. My mind went to Moodle, where a similar feature exists. This is what they do.
From cron.php:
...
/// The current directory in PHP version 4.3.0 and above isn't necessarily the
/// directory of the script when run from the command line. The require_once()
/// would fail, so we'll have to chdir()
if (!isset($_SERVER['REMOTE_ADDR']) && isset($_SERVER['argv'][0])) {
chdir(dirname($_SERVER['argv'][0]));
}
...
/// check if execution allowed
if (isset($_SERVER['REMOTE_ADDR'])) { // if the script is accessed via the web.
if (!empty($CFG->cronclionly)) {
// This script can only be run via the cli.
print_error('cronerrorclionly', 'admin');
exit;
}
// This script is being called via the web, so check the password if there is one.
if (!empty($CFG->cronremotepassword)) {
$pass = optional_param('password', '', PARAM_RAW);
if($pass != $CFG->cronremotepassword) {
// wrong password.
print_error('cronerrorpassword', 'admin');
exit;
}
}
}
...
You need a PHP CLI/CGI executable for that. Assuming that the php program is located at /usr/local/bin/php, you can use:
/usr/local/bin/php /path/to/your/script.php
See also: http://nl.php.net/manual/en/features.commandline.usage.php
Please add this script at the top of your PHP file:
$isCLI = ( php_sapi_name() == 'cli' );
if( !$isCLI )
die("Sorry! Cannot run in a browser! This script is set to run via cron job");
and then if you try to run the PHP file via the browser, you cannot run it. This error message will be displayed. But at the same time, it can be run via a cron job.
Try to grant execute permissions only for the cron daemon user, maybe with that you get what you want.
Regards!
I'm writing a PHP script that I want to disable from web access (I will ask users to move it out of the web root and execute via CLI, but you never know if they'll listen!)
Is there a simple function that occurs to anyone to make the page die if it's requested by a browser?
Thanks for any ideas.
You could test whether the script is being run through the CLI using php_sapi_name().
It can return a whole bunch of different possible values when run on a HTTP server - difficult to make a reliable distinction there - but there seems to be only one possible return value for the CLI: cli.
If you're looking for an all-purpose solution, make sure you read the comment thread below for more detailed discussion on some potential gotchas.
'PHP_SELF' The filename of the
currently executing script, relative
to the document root. For instance,
$_SERVER['PHP_SELF'] in a script at
the address
http://example.com/test.php/foo.bar
would be /test.php/foo.bar. The
FILE constant contains the full path and filename of the current (i.e.
included) file. If PHP is running as a
command-line processor this variable
contains the script name since PHP
4.3.0. Previously it was not available.
http://www.php.net/manual/en/reserved.variables.server.php
I'm not a PHP expert but you could check the $_SERVER variable ?
You can use php-sapi-name function to detect if script was requested by web server or cli interface.
$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) != 'cli') {
die "You are not using CLI".PHP_EOL;
}
you can use php-sapi-name or you can use the predefined constant which is marginally faster (although you'd never notice!)
<?php
if(PHP_SAPI != 'cli') exit;
// continue