-1

I need to allow php scripts to be called only via Ajax and Cron, and not called via it's address in the browser.

In F12 all the php scripts are visible, and any user may call the php script like website.com/file.php

Sonya Seyrios
  • 83
  • 1
  • 9
  • Possible duplicate of [Deny access to specific file types in specific directory](http://stackoverflow.com/questions/16108399/deny-access-to-specific-file-types-in-specific-directory) – Duane Lortie Mar 22 '17 at 23:21
  • Look at http://stackoverflow.com/questions/16108399/deny-access-to-specific-file-types-in-specific-directory – Duane Lortie Mar 22 '17 at 23:21
  • There is no answer to my question. I need to allow Ajax and Cron to access that files – Sonya Seyrios Mar 22 '17 at 23:23

1 Answers1

0

You cannot. Scripts do not know how they are called; all they know is that the web server has them invoked.

And the web server does not know who is it that is calling, just that a request came by on the HTTP or HTTPS port (cron CLI is an exception, see below).

The best you can do is verify these two headers:

HTTP_USER_AGENT            either empty or filled
HTTP_X_REQUESTED_WITH      either 'XMLHttpRequest' or not (possibly empty)

The results will be:

UA empty, XRW empty              cron or a browser faking cron
UA "Lynx" or "wget", XRW empty   cron calling wget or lynx (or curl?)
UA empty, XRW filled             some prankster messing with you (*)
UA filled, XRW filled            AJAX call
UA filled, XRW empty             browser

(*) or some "privacy" client firewall removing the User-Agent header

But do remember that:

  • a browser can fake being cron (e.g. using ModifyHeaders plugin)
  • cron can fake being a browser (why? dunno. But it's possible)
  • anyone can fake anything (e.g. via curl)

You would be better off checking e.g.

HTTP_REMOTE_ADDR

which is not set when calling via cron CLI, and when set, should be the IP address of the servers you authorize, as seen by the Web server.

Another possibility is to use authentication, e.g. via challenge/response with a shared secret between the calling script and the called PHP program.

Other possibilities

I'm going out on a limb here, and imagining that your situation is this:

You have a script that you need to be called periodically, or when a user does something via AJAX, but not too often (maybe because it's computationally expensive). So you call the script from cron once a day, and whenever some user looks at a page, but do not want the user to see the call and be able to replicate it at will, maybe even from a different browser or a cron of their own.

A simple solution is to do it the other way round. You can set a variable in the session:

// AJAX calls are only allowed every 600 seconds PER EACH USER
// (you should verify that a login exists)
if (array_key_exists('ajax', $_SESSION)) {
    if (time() - $_SESSION['ajax'] < 600) {
        return;
    }
}
$_SESSION['ajax'] = time();

Or you can limit the calls using a file:

if (file_exists('call.txt')) {
    if ((time() - filemtime('call.txt')) < 600) {
        return;
    }
}
touch('call.txt');

Now the script cannot be called more often than once every 600 seconds (10 minutes), no matter how (except for cron CLI which will almost surely create the call.txt in a different directory).

You can perhaps also store the script output in a file, and read the file's contents if it's not older than a given value. So you can issue one thousand AJAX calls, but only the first will do any actual work - the other will wait on the locked file and then read a cached copy. This is trickier than it appears due to the need of dealing with race conditions between possibly several parallel calls.

LSerni
  • 55,617
  • 10
  • 65
  • 107
  • Thanks a lot, but I still don't know what to do) – Sonya Seyrios Mar 22 '17 at 23:31
  • What do you need this check for? Is it for security? Maybe what you want to achieve can be achieved in another way. For example, why you need the same script to be accessible from *cron* and from AJAX? It looks strange. – LSerni Mar 22 '17 at 23:33
  • All .php files are visible in F12 `XHR` tab. So, a user may just click on php file and call it, just for fun. BTW this is also the question- how to prevent them from being visible? – Sonya Seyrios Mar 22 '17 at 23:36
  • cron calls the script each day. however, user may call it himself, clicking on a button – Sonya Seyrios Mar 22 '17 at 23:38
  • Your update doesn't answer my question: I want to prevent the users to call my php files via the browser address tab, that's all) – Sonya Seyrios Mar 22 '17 at 23:48
  • Are you prepared to accept the fact that you cannot prevent all of them, but only the less knowledgeable? – LSerni Mar 22 '17 at 23:53
  • The users may access some php files, which they don't have access via the website. And I have to find solutions – Sonya Seyrios Mar 22 '17 at 23:56
  • You did not answer my question ;-). What control do you have on those files? Using only .htaccess limits our options. – LSerni Mar 23 '17 at 00:00
  • I have full access – Sonya Seyrios Mar 23 '17 at 00:04
  • So why don't you use the X_REQUESTED_WITH, USER_AGENT and REMOTE_ADDR fields? – LSerni Mar 23 '17 at 00:07
  • Should I add them to each .php file? :) – Sonya Seyrios Mar 23 '17 at 00:08
  • Only on those that require the protection. You can prepare a single 'protect.php' file and include it from everywhere else. Or you can use the PHP auto_prepend directive in a .htaccess file, provided that the scripts are all in the same directory, separated from the others (i.e. no protected file shares a directory with an unprotected file). You can dump those values to output in a script, and call that script from AJAX, from cron, and from a browser, so you'll see the differences in the three cases. Then you can code for those. – LSerni Mar 23 '17 at 00:13
  • Ok, `UA empty, XRW filled` option is enough for my scenario? – Sonya Seyrios Mar 23 '17 at 00:14
  • It should. UA empty means it's cron, and XRW filled means it's AJAX. Unless some smart-ass deploys tricks like the ModifyHeaders plugin. But you can also see what values are in HTTP_REMOTE_ADDR and HTTP_REFERER. When calling from a browser after pasting the URL, the referer should be empty. When calling from AJAX you should find there the address of the originating page. – LSerni Mar 23 '17 at 00:40
  • Probably, quicker solution: http://stackoverflow.com/questions/33999475/prevent-direct-url-access-to-php-file – Sonya Seyrios Mar 23 '17 at 01:02
  • Yes, if you use POST for your AJAX. And, now that I come to think of it, what happens if a user selects 'Edit and Resend' in the F12 Webmaster tools tab? (I think it's installed by default in both Firefox and Chrome). That will do a perfect resend, and go around any htaccess or header-based security. – LSerni Mar 23 '17 at 01:05
  • Also, it's possible to access via command line (which is similar to cronjob) – Sonya Seyrios Mar 23 '17 at 01:14
  • Well yes, but your users shouldn't have access to the shell on your server ;-) – LSerni Mar 23 '17 at 01:23
  • Now the problem is that, .htaccess removes .php from the URL. Therefore, `$_SERVER['REQUEST_METHOD']=='GET' && realpath(__FILE__) == realpath($_SERVER['SCRIPT_FILENAME'])` doesn't work – Sonya Seyrios Mar 23 '17 at 01:26
  • `preg_replace('/\\.[^.\\s]{3,4}$/', '', $filename);` ;) – Sonya Seyrios Mar 23 '17 at 01:29