Do you have scheduled jobs run via Cron that you want to prevent being run via browsers and other user agents?
Well, here are two single line statements to do just that. One, for scripts “requested” by directory path; and another for scripts requested by URL.
Make sure you also read the “Notes section” which identifies limitations, issues, and an alternative solution. The usual caveat: use advice and code on this page at your own risk. It works for me; but I can’t guarantee it will work, or that it is safe for use on your site.
If you are executing PHP directly from the Cron Job e.g. php /path/to/your_script.php then add the following line at the top of your PHP script:
if (php_sapi_name() !='cli') exit;
If the Cron Job uses wget, curl or lynx to run your script via its URL, then insert this code at the top of your PHP script (change the User Agent string to one known only by you) :
if ($_SERVER['HTTP_USER_AGENT'] != 'yourSecretAgent') exit;
You will also have to set the User Agent in the Cron Job; as in this wget example:
-O - --user-agent=“yourSecretAgent” http://example.com/your_script.php
Comments on Method 1:
Test before use: In nearly all cases php_sapi_name() will return “cli” if running PHP from a Cron. However, user comments at php.net indicate that this may not be the case for some servers. Write a single line script to echo php_sapi_name(), and check what string is returned when run from cron and from browser. See PHP.Net’s explanation of php_sapi_name() and user comments.
Checking for “cli” (command line interface) will block “HTTP” (browser) access which is what we want, but won’t prevent running via Terminal or shell script. If you are in a multi user environment and concerned about unauthorised CLI use then see this suggestion at Stackoverflow; however the suggested user/owner changes may not be feasible if you are using a shared server.
Ideally you should locate your script in a directory outside the “document root” which contains your web pages. This will also prevent the script being run via a “normal” URL – in which case you may ask why bother checking if the script is being run via Cron? Well, the location of your script won’t protect you from, for example:
- URL directory traversal e.g. http://example.com/%2E%2E%2FyourCronScript.php (if this applies to you then install the last xx years web server patches or change your hosting provider ASAP); and
- null byte attacks injecting your script’s file path and name into “require” or “include” statements. This attack relies on exploiting vulnerabilities in your own, or third party code. PHP 5.3.4 onward protects against this by treating null bytes in paths as invalid.
Checking for CLI using php_sapi_name() should block these types of attack (but, obviously, it is not a magic bullet that will defend you from every type of exploit).
Comments on Method 2:
The User Agent is passed in the HTTP header and can be spoofed. If (a massive if) the “attacker” knows your secret user agent string he can still run your script. Highly unlikely.
As we are only conducting a comparison, the code for Method 2 example does not sanitise the User Agent (UA). Ordinarily, you should treat information provided in the HTTP header (such as UA and referrer) with the same caution as other input; and sanitise/validate/escape as necessary. The UA can be “poisoned” e.g. for use in an XSS attack.
If you are going to use the UA within your script, or store it for say display on a log page; then follow the Sanitise on input and/or escape on output rule. This Smashing Magazine article and its follow up provide a great introduction into safely managing input and output within PHP scripts.
As always comments are welcome.
Andy Wrigley+ has worked in IT and Computer Audit for 30 years, and loves independent travel.