source: trunk/www/utilities.php @ 1184

Revision 1174, 9.5 KB checked in by adrian.budau, 3 months ago (diff)

Bunch of security fixes.

Login and register pages go through https
Updated recaptcha library

Implmented an anti-spam token system with price per actions and
token regeneration.

There is a maximum amount of tokens per IP.
Actions like register and login cost tokens.
When there are not enough tokens a captcha is requested.
The tokens regenerate at a constant rate.

Example -> This is how the captcha is requested at this moment for register/login:

You can login/logout as manytimes as you want. If you do 3 bad login attempts a captcha will appear and will be requested until you login correctly.
You always need a captcha for registering and after that after only one bad login attempt a captcha will be requested.
You can logout and login a different account without the need of a captcha(there is no way to use this as a brute-force entrance).

Token system description below:

You can communicate with the tokens system with the functions
get_tokens to get current tokens
check_captcha_for_tokens to check for captcha submits and their correctness thus adding an amount of tokens, this function also returns the error of the captcha(it can be forced to search for all errors)
pay_tokens which pays a certain amount of tokens or receives(if used with a negative value), it returns true or false weather it can pay or not(has enough)
save_tokens(which pushes the tokens to the mysql db)

Review URL: http://reviewboard.infoarena.ro/r/188/

  • Property svn:eol-style set to native
Line 
1<?php
2
3require_once(IA_ROOT_DIR . 'www/url.php');
4require_once(IA_ROOT_DIR . 'common/db/tokens.php');
5// Wrapper around htmlentities which defaults charset to UTF-8
6function html_escape($string, $quote_style = ENT_COMPAT, $charset = "UTF-8")
7{
8    return htmlentities($string, $quote_style, $charset);
9}
10
11function xml_escape($string, $quote_style = ENT_COMPAT, $charset = "UTF-8")
12{
13    $xml = array('&#34;','&#38;','&#38;','&#60;','&#62;','&#160;','&#161;',
14        '&#162;','&#163;','&#164;','&#165;','&#166;','&#167;','&#168;','&#169;',
15        '&#170;','&#171;','&#172;','&#173;','&#174;','&#175;','&#176;','&#177;',
16        '&#178;','&#179;','&#180;','&#181;','&#182;','&#183;','&#184;','&#185;',
17        '&#186;','&#187;','&#188;','&#189;','&#190;','&#191;','&#192;','&#193;',
18        '&#194;','&#195;','&#196;','&#197;','&#198;','&#199;','&#200;','&#201;',
19        '&#202;','&#203;','&#204;','&#205;','&#206;','&#207;','&#208;','&#209;',
20        '&#210;','&#211;','&#212;','&#213;','&#214;','&#215;','&#216;','&#217;',
21        '&#218;','&#219;','&#220;','&#221;','&#222;','&#223;','&#224;','&#225;',
22        '&#226;','&#227;','&#228;','&#229;','&#230;','&#231;','&#232;','&#233;',
23        '&#234;','&#235;','&#236;','&#237;','&#238;','&#239;','&#240;','&#241;',
24        '&#242;','&#243;','&#244;','&#245;','&#246;','&#247;','&#248;','&#249;',
25        '&#250;','&#251;','&#252;','&#253;','&#254;','&#255;', '&#8221;', 
26        '&#8222;',
27    );
28    $html = array('&quot;','&amp;','&amp;','&lt;','&gt;','&nbsp;','&iexcl;',
29        '&cent;','&pound;','&curren;','&yen;','&brvbar;','&sect;','&uml;',
30        '&copy;','&ordf;','&laquo;','&not;','&shy;','&reg;','&macr;','&deg;',
31        '&plusmn;','&sup2;','&sup3;','&acute;','&micro;','&para;','&middot;',
32        '&cedil;','&sup1;','&ordm;','&raquo;','&frac14;','&frac12;','&frac34;',
33        '&iquest;','&Agrave;','&Aacute;','&Acirc;','&Atilde;','&Auml;','&Aring;',
34        '&AElig;','&Ccedil;','&Egrave;','&Eacute;','&Ecirc;','&Euml;','&Igrave;',
35        '&Iacute;','&Icirc;','&Iuml;','&ETH;','&Ntilde;','&Ograve;','&Oacute;',
36        '&Ocirc;','&Otilde;','&Ouml;','&times;','&Oslash;','&Ugrave;','&Uacute;',
37        '&Ucirc;','&Uuml;','&Yacute;','&THORN;','&szlig;','&agrave;','&aacute;',
38        '&acirc;','&atilde;','&auml;','&aring;','&aelig;','&ccedil;','&egrave;',
39        '&eacute;','&ecirc;','&euml;','&igrave;','&iacute;','&icirc;','&iuml;',
40        '&eth;','&ntilde;','&ograve;','&oacute;','&ocirc;','&otilde;','&ouml;',
41        '&divide;','&oslash;','&ugrave;','&uacute;','&ucirc;','&uuml;','&yacute;',
42        '&thorn;','&yuml;', '&rdquo;', '&bdquo;',
43    );
44    $string = html_escape($string, $quote_style, $charset);
45    $string = str_replace($html, $xml, $string);
46    $string = str_ireplace($html, $xml, $string);
47    return $string;
48}
49
50// returns an array of all arguments in REQUEST
51function request_args() {
52    $result = array();
53    foreach($_REQUEST as $key => $value) {
54        $result[] = $key;
55    }
56    return $result;
57}
58
59function request($param, $default = null) {
60    return getattr($_REQUEST, $param, $default);
61}
62
63// Returns boolean whether current request method is POST
64function request_is_post() {
65    return ('post' == strtolower(getattr($_SERVER, 'REQUEST_METHOD')));
66}
67
68// Call this function for a http-level redirect.
69// NOTE: this function DOES NOT RETURN.
70//
71// NOTE: this must be called before any other output.
72// If output started before issuing a redirect means you're either
73// printing stuff too early or you're trying to redirect too late (view?).
74// Either way, it is a bug and it must be solved rather than handled gracefully
75//
76// FIXME: bool to se ia_redirect to REQUEST_URI? might be usefull.
77function redirect($absolute_url) {
78    header("Location: {$absolute_url}\n\n");
79    session_write_close();
80    save_tokens();
81    die();
82}
83
84// Checks if the referer is the same as the host
85function http_referer_check() {
86    return true;
87    //FIXME: this is broken
88    $HTTP_REFERER = getattr($_SERVER, 'HTTP_REFERER');
89    $HTTP_HOST = getattr($_SERVER, 'HTTP_HOST');
90    return $HTTP_REFERER==null || substr($HTTP_REFERER, 0, (strlen($HTTP_HOST)+7)) == "http://".$HTTP_HOST;
91}
92
93// Client side caching... let's save some bandwidth
94// If you call this and the client has a version which is newer that $last_modified
95// then the request aborts.
96// Otherwise the client is told to only ask again after $cache_age seconds.
97//
98// This function analyzes http headers and looks for an If-Modified-Since header.
99function http_cache_check($last_modified, $cache_age = IA_CLIENT_CACHE_AGE) {
100    if (!IA_CLIENT_CACHE_ENABLE) {
101        return;
102    }
103
104    $headers = apache_request_headers();
105    if (isset($headers['If-Modified-Since'])) {
106        // we split it due to some bug in Mozilla < v6
107        $modified_since = explode(';', $headers['If-Modified-Since']);
108        $modified_since = strtotime($modified_since[0]);
109    } else {
110        $modified_since = 0;
111    }
112
113    // Serve HTTP headers to cache file
114    header("Cache-Control: max-age: ".IA_CLIENT_CACHE_AGE
115           ." , public, must-revalidate");
116    // Additional headers, obsolete in HTTP 1.1. browsers
117    header('Expires: '.gmdate('D, d M Y H:i:s',
118              time()+IA_CLIENT_CACHE_AGE).' GMT');
119
120    if ($last_modified !== false && $modified_since >= $last_modified) {
121        // Client's cache is up to date, yey!
122        header('Last-Modified: '.gmdate('D, d M Y H:i:s', $last_modified)
123               .' GMT', true, 304);
124        //log_print('CACHE: Client hit');
125        die();
126    } else {
127        //log_print('CACHE: Client miss');
128        // Client's cache is missing / out-dated
129        header('Last-Modified: '.gmdate('D, d M Y H:i:s', $last_modified)
130               .' GMT', true, 200);
131    }
132}
133
134// Serve static file through HTTP
135// NOTE: cache check enabled by default
136// WARNING: this function does not return
137function http_serve($disk_file_name, $http_file_name, $mime_type = null, $cache_check = true) {
138    if (is_null($mime_type)) {
139        $mime_type = "application/octet-stream";
140    }
141
142    global $IA_SAFE_MIME_TYPES;
143    if (!in_array($mime_type, $IA_SAFE_MIME_TYPES)) {
144        $disposition = "attachment";
145
146        // WARNING: *don't* add cache or the second time an attachment is downloaded in IE it will load inline
147    } else {
148        $disposition = "inline";
149
150        // Cache magic.
151        if ($cache_check) {
152            http_cache_check(filemtime($disk_file_name));
153        }
154    }
155
156    // HTTP headers. 
157    header("Content-Type: {$mime_type}");
158    header("Content-Disposition: {$disposition}; filename="
159           .urlencode($http_file_name).";");
160    $fsize = filesize($disk_file_name);
161    header("Content-Length: " . $fsize);
162
163    $fp = fopen($disk_file_name, "rb");
164    log_assert($fp);
165
166    // Serve file
167    $written = fpassthru($fp);
168    if ($written != $fsize) {
169        log_error("fpassthru failed somehow.");
170    }
171    fclose($fp);
172    die();
173}
174
175// Die with a http error.
176function die_http_error($code = 404, $msg = "File not found") {
177    header("HTTP/1.0 $code");
178    echo '<h1>'.$msg.'</h1>';
179    echo '<p><a href="'.IA_URL.'">Inapoi la prima pagina</a></p>';
180    die();
181}
182
183// Use flash() to display a message right after redirecting the user.
184// Message is displayed only once.
185function flash($message, $style_class = null) {
186    global $_SESSION;
187    $_SESSION['_ia_flash'] = $message;
188    $_SESSION['_ia_flash_class'] = $style_class;
189}
190
191// This is a simple binding for flash() with a fixed CSS style class
192// for displaying error messages
193function flash_error($message) {
194    flash($message, 'flashError');
195}
196
197// Execute a view. Variables in $view are placed in the
198// local namespace as variables. This is the preffered
199// way of calling a template, because globals are not
200// easily accessible.
201function execute_view($view_file_name, $view) {
202    global $identity_user;
203
204    // retrieve recent page history
205    // some pages display it as navigation breadcrumbs
206    $recent_pages = getattr($_SESSION, '_ia_recent_pages', array());
207
208    // update recent page history
209    $query = url_from_args($_GET);
210    if (!preg_match('/\/(json|plot|changes)\//', $query) && !request_is_post()) {
211        $hashkey = strtolower($query);
212        $recent_pages[$hashkey] = array($query, getattr($view, 'title', $query)); 
213        if (5 < count($recent_pages)) {
214            array_shift($recent_pages);
215        }
216        $_SESSION['_ia_recent_pages'] = $recent_pages;
217    }
218
219    // let view access recent_pages
220    $view['current_url_key'] = strtolower($query);
221    $view['recent_pages'] = $recent_pages;
222
223    // give access to request statistics
224    if (IA_DEVELOPMENT_MODE) {
225        global $execution_stats;
226        $view['execution_stats'] = $execution_stats;
227    }
228
229    // expand $view members into global scope
230    $GLOBALS['view'] = $view;
231
232    foreach ($view as $view_hash_key => $view_hash_value) {
233        if ($view_hash_key == 'view_hash_key') continue;
234        if ($view_hash_key == 'view_hash_value') continue;
235        if ($view_hash_key == 'view_file_name') continue;
236        if ($view_hash_key == 'view') continue;
237        //echo "added $view_hash_key = $view_hash_value into globals";
238        $GLOBALS[$view_hash_key] = $view_hash_value;
239        $$view_hash_key = $view_hash_value;
240    }
241
242    // NOTE: no includes here, unless you want to get
243    // warnings about function redeclaration.
244    include($view_file_name);
245}
246
247// Execute view and then die.
248function execute_view_die($view_file_name, $view) {
249    execute_view($view_file_name, $view);
250    if (IA_DEVELOPMENT_MODE) {
251        log_execution_stats();
252    }
253    session_write_close();
254    save_tokens();
255    die();
256}
257
258?>
Note: See TracBrowser for help on using the repository browser.