changed git call from https to git readonly
[atutor.git] / mods / wiki / fragments / site_prot.php
1 <?php
2
3    site_prot();
4
5 #  This include() script implements anti-leech and anti-bot functionality,
6 #  and can probably be used with any web site, and is especially not tied
7 #  to ewiki. Its approach isn't proxy friendly, and therefore will likely
8 #  knock out AOL users and the like.
9 #  This script can be used concurrently on a shared webserver (common for
10 #  nowadays web space providers), because the the version numbers protect
11 #  from broken lock data - and after all sharing the data only can benefit
12 #  this protection system.
13 #
14 #  You can knock out a user (if you detect unwanted behaviour) by just
15 #  calling site_prot(+1); from anywhere in yoursite.
16
17
18 function site_prot($bad_behaviour_detected=0) {
19
20    #-- config
21    $max_hits = 1000;           # absolute hit-limit per IP
22    $rm_stale_locks = 120;      # seconds, after which lock files invalidate
23    $delay = 3;                 # request slow down time (bot brake)
24    $a_ratio = 5/1;             # max allowed accesses per second
25    $ignore_humans = 1;         # humans may perform all requests
26 $friends="/|127.0.0.1|216.239.*.*|64.68.*.*|204.123.*.*|204.152.*.*|128.177.*.*||";
27
28    #-- bots rarely send Cookies, and also avoid the POST request method
29    $not_a_bot = count($_COOKIE)
30              || ($_SERVER["REQUEST_METHOD"] == "POST");
31
32    #-- this allows us to ignore non-ewiki-page-requests
33    $ignore_hit = (@$_REQUEST["binary"])  // image and _BINARY database entries
34              || (@$_REQUEST["q"])        // PowerSearch
35              || (@$_REQUEST["**************"]);
36    if ($ignore_hit) {
37       return(NULL);
38    }
39
40    #-- directory for lock files
41    (defined("EWIKI_TEMP") and ($tmp=EWIKI_TEMP))
42    or ($tmp = @$_SERVER["TEMP"]) or ($tmp = @$_SERVER["TEMP_DIR"])
43    or ($tmp = "/tmp");
44    $tmp .= "/site_prot/";
45    if (!file_exists($tmp)) {
46       mkdir($tmp, 0770);
47    }
48
49    #-- aggressor
50    ($ip = $_SERVER["REMOTE_ADDR"])
51    or ($ip = $_SERVER["X_FORWARDED_FOR"]);   # or even start traceroute here?
52    $half_ip = substr($ip, 0, @strpos($ip, ".", 4));   # (incorrect, but works for the default $friends list)
53    if (strpos($friends, "|$ip|") || strpos($friends, "|$half_ip.*.*|")) {
54       return(+5);
55    }
56
57    #-- data file
58    $lockfile = $tmp . strtr($ip, ":", "+");
59    $data = array(
60       1,           // hits
61       time(),      // last_modified
62       time(),      // creation_time
63       0,           // bad_guest
64       0,           // is_no_bot
65    );
66    $data[3] |= ((int)$bad_behaviour_detected);
67    $data[4] |= ($not_a_bot?1:0);
68
69    #-- read info about guest
70    if (file_exists($lockfile)) {
71       $last_access = filemtime($lockfile);
72       $first_access = filectime($lockfile);
73
74       #-- remove old lockfile
75       if (($ignore_humans && $not_a_bot) || (@$_REQUEST["site_prot_unlock"])
76       || ($last_access + $rm_stale_locks < time())) {
77          unlink($lockfile);
78          return(+0);
79       }
80
81       #-- read-in and update data
82       if ($f = fopen($lockfile, "r+")) {
83          if ($data = unserialize(fread($f, 65536))) {
84             $data[0] += 1;
85             $data[1] = time();
86             $data[4] |= ($not_a_bot?1:0);
87             fseek($f, 0);
88             fwrite($f, serialize($data));
89          }
90          fclose($f);
91       }
92
93       #-- keep annoying the bad guys
94       if ($data[3]) {
95          site_prot_trap($delay << 1);
96       }
97
98       #-- check for too many requests,
99       #   lockfile usually were already deleted after that many requests
100       #   (this is the proxy trap)
101       if ($data[0] >= $max_hits) {
102          site_prot_trap($delay);
103       }
104
105       #-- ignore following checks for humans
106       if ($ignore_humans && $data[4]) {
107          return(+1);
108       }
109
110       #-- too many requests per time,
111       #   measured according to the time slice the current lockfile exists
112       if ($data[0] > ($data[1]-$data[2]) / $a_ratio) {
113          site_prot_trap($delay);
114       }
115
116       return(+2);
117    }
118    else {
119       if ($f = fopen($lockfile, "w")) {
120          fwrite($f, serialize($data));
121          fclose($f);
122
123          return(+1);
124       }
125    }
126
127    return(-1);
128 }
129
130
131 #-- called if we want to stop the client
132 function site_prot_trap($delay=3) {
133
134    #-- we want to slow down the bot, but not ourselfes
135    ignore_user_abort(0);
136    set_time_limit(30);
137
138    #-- disable any establish output buffering
139    if (function_exists("ob_end_clean")) {
140       while (function_exists("ob_get_level") && ob_get_level() || ob_get_length()) {
141          ob_end_clean();
142       }
143    }
144
145    #-- send out a warning message for humans (to also allow for lock removal)
146    echo<<<EOF
147 <!--[site_prot_trap()]-->
148 <h1>Lock</h1>
149 Your <a href="http://google.com/search?q=IP+address">IP</a>
150 has been locked and you cannot make further<br>
151 requests to this site.<br>
152 <br>
153 <form action="{$_SERVER[REQUEST_URI]}" method="POST" enctype="application/x-www-form-urlencoded">
154 <input type="checkbox" name="site_prot_unlock" value="true"> No, please
155 <input type="submit" name="site_prot_unlock_button" value="unlock me!!">
156 </form>
157 EOF;
158    flush();
159
160    #-- should we flood our syslog here?
161    /*
162      ...
163    */
164
165    #-- slow down the bot, and exit the script
166    sleep($delay);
167    die(34);
168 }
169
170
171 ?>