Собсно вот. В продолжении темы о шаблонизаторах и прочем. Прошу рассказать о возможных уязвимостях и о том, как оптимизировать код. Ну и о переводе его на шаблонизатор. index.php PHP: <?php define("MAIN_FILE", true); include'core/mysql.php'; include'core/auth.php'; showLogInForm('admin'); echo isAdmin(); ?> <style type="text/css"> form { margin: 0; } </style> auth.php PHP: <?php if (!defined("MAIN_FILE")) die("Illegal File Access"); define("COOKIE_SALT", 'abcdef'); define("PRIV_SALE_REGISTER", 1); define("PRIV_SALE_SHOW", 2); define("PRIV_SALE_EDIT", 3); define("PRIV_SALE_CONFIRM", 4); /**/ function isUser(){ global $db; $result = $db->sql_query("SELECT name, password, salt, privileges FROM mypr_user WHERE id='".$_COOKIE['id']."'"); list($name, $passwordDB, $salt, $privileges) = $db->sql_fetchrow($result); if (md5($passwordDB . COOKIE_SALT)==$_COOKIE['user']) { return $privileges; } return false; } /**/ function isAdmin(){ global $db; $result = $db->sql_query("SELECT * FROM mypr_admin WHERE id='".$_COOKIE['id']."'"); list($id, $name, $passwordDB, $salt, $privileges) = $db->sql_fetchrow($result); if (md5($passwordDB . COOKIE_SALT)==$_COOKIE['admin']) { return $privileges; } return false; } /**/ function logIn($logInType, $name, $password){ global $db; switch($logInType){ case "admin": $result = $db->sql_query("SELECT * FROM mypr_admin WHERE name='".$name."'"); list($id, $name, $passwordDB, $salt, $privileges) = $db->sql_fetchrow($result); if (md5(md5($password) . $salt)==$passwordDB) { setcookie('admin', md5($passwordDB . COOKIE_SALT), time()+86400); setcookie('id', $id, time()+86400); } break; case "user": $result = $db->sql_query("SELECT id, name, password, salt FROM mypr_user WHERE name='".$name."'"); list($id, $name, $passwordDB, $salt) = $db->sql_fetchrow($result); if (md5(md5($password) . $salt)==$passwordDB) { setcookie('user', md5($passwordDB . COOKIE_SALT), time()+86400); setcookie('id', $id, time()+86400); } break; } header("Location: index.php"); } /**/ function logOut($logInType){ switch($logInType){ case "admin": setcookie('admin', '', time()-86400); break; case "user": setcookie('user', '', time()-86400); break; } } /**/ function showLogInForm($logInType='user'){ ?> <div id="divLogInForm" style="text-align:center"> Представьтесь, пожалуйста. <form name="logInForm" id="logInFormID" action="?do=logIn" method="post"> Имя пользователя: <input type="text" name="name"><br /> Пароль: <input type="password" name="password"><br /> <? if ($logInType=='admin') { ?> <input type="hidden" name="admin" value="yes"> <? } ?> <input type="submit" value="Вход"> </form> </div> <? } switch($_GET['do']) { case "logIn": $logInType = ($_POST['admin']=='yes') ? 'admin' : 'user'; logIn($logInType, $_POST['name'], $_POST['password']); break; } ?>
Koc Данные от пользователя не обрабатываются никак перед тем, как начать чтение из БД по указанным параметрам (форма, cookie). А так, вроде, все нормально
Elkaz да, вот и мне внутренний голос говорит, что нужно как-то фильтровать "плохие символы" перед тем как с базой сравнивать. Но как именно я не знаю. А что с глобальными переменными делать? заменить PHP: <?logIn($logInType, $_POST['name'], $_POST['password']);?> на PHP: <?logIn($logInType, $_POST['name'], $_POST['password'], $db);?> ? В догонку функция запроса в БД: PHP: <? function sql_query($query = "", $transaction = false) { unset($this->query_result); if ($query != "") { $st = array_sum(explode(" ", microtime())); $this->query_result = @mysql_query($query, $this->db_connect_id); $total_tdb = round(array_sum(explode(" ", microtime())) - $st, 5); $this->total_time_db += $total_tdb; $this->time_query .= "".$total_tdb > 0.01."" ? "<font color=\"red\"><b>".$total_tdb."</b></font> "._SEC.". - [".$query."]<br /><br />" : "<font color=\"green\"><b>".$total_tdb."</b></font> "._SEC.". - [".$query."]<br /><br />"; } if ($this->query_result) { $this->num_queries += 1; unset($this->row[$this->query_result]); unset($this->rowset[$this->query_result]); return $this->query_result; } else { return ($transaction == END_TRANSACTION) ? true : false; } } ?>
Koc У меня лично есть своя волшебная функция, которую я пользуюсь. К примеру, мне в имени пользователя не нужны html. Ну совсем. Мне вообще от пользователя html не нужен - я сразу его вырезаю при помощи htmlspecialchars. А данные, которые записываются в БД лучше экранировать при помощи mysql_real_escape_string ) Горбунов Олег А почему бы служебные, которые НАДО экранировать, не называть плохими? Это субъективная точка зрения.
http://ua.php.net/mysql_real_escape_string PHP: <?php // Функция экранирования переменных function quote_smart($value) { // если magic_quotes_gpc включена - используем stripslashes if (get_magic_quotes_gpc()) { $value = stripslashes($value); } // Если переменная - число, то экранировать её не нужно // если нет - то окружем её кавычками, и экранируем if (!is_numeric($value)) { $value = "'" . mysql_real_escape_string($value) . "'"; } return $value; } // Соединяемся $link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') OR die(mysql_error()); // Составляем безопасный запрос $query = sprintf("SELECT * FROM users WHERE user=%s AND password=%s", quote_smart($_POST['username']), quote_smart($_POST['password'])); mysql_query($query); ?> понравилось. upd: Хотя имхо это параноя так защищаться от инъекции. Как бы упростить решение, не пренебрегая безопастностью?? upd2: Хотя, чтоб не стать быдлокодером, наверно нужно так и делать.. [чешет затылок]
Так никто не заставляет... Пожалуйста, используйте [sql]SELECT * FROM `table` WHERE login = '".$_POST['login']."'[/sql] Насильно никто не заставляет. Сами поймете, со временем
вот сваял кой-че: PHP: <?php function clean($variable) { if(is_numeric($variable)) return $variable; if(!get_magic_quotes_gpc()) { if(is_array($variable)){ foreach($variable as $k=>$v){ $variable[$k]=clean($v); } return $variable; } return "'" . mysql_real_escape_string($variable) . "'"; } return "'" . $variable . "'"; } ?> Код (Text): запускаю http://localhost/?aa=aa'aa&bb[11][22]=aa'3`3 проверяю PHP: <?php echo $_GET['aa'],'<br />'; echo clean($_GET['aa']),'<br />'; echo $_GET['bb'][11][22],'<br />'; $tmp = clean($_GET['bb']); echo $tmp[11][22],'<br />'; ?> php_flag magic_quotes_gpc off php_flag magic_quotes_runtime off aa'aa 'aa\'aa' aa'3`3 'aa\'3`3' php_flag magic_quotes_gpc on php_flag magic_quotes_runtime on aa\'aa 'aa\'aa' aa\'3`3 'aa\'3`3' It working! Задался вопросом: mysql_real_escape_string или addslashes ? что лучше, что быстрее? Думаю, что второе, так как не требуется загружать расширение для работы с БД.
PHP: <? //это $result = $db->sql_query("SELECT * FROM mypr_admin WHERE id='".$_COOKIE['id']."'"); $result = $db->sql_query("SELECT id, name, password, salt FROM mypr_user WHERE name='".$name."'"); //заменил на это $sql = sprintf("SELECT * FROM mypr_admin WHERE id=%d", $_COOKIE['id']); $result = $db->sql_query($sql); $sql = sprintf("SELECT id, name, password, salt FROM mypr_user WHERE name=%s",clean($name)); $result = $db->sql_query($sql); ?> ну как? так правильно?
нет. Его sprintf преобразует в int. Или не преобразует? Немного изменил код: PHP: <?php // $rev 20.05.08 // экранирует спецсимволы внутри переменной вне зависимости от // настроек magic_quotesи обрамляет переменную одинарными кавычками, // если она не является числом function cleanQ($variable) { // $rev 20.05.08 //обрамляет переменную одинарными кавычками. Видна только из cleanQ if (!function_exists('addQ')) { function addQ($variable){ if(is_array($variable)){ foreach($variable as $k=>$v){ $variable[$k]=addQ($v); } return $variable; } return "'" . $variable . "'"; } } if(is_numeric($variable)) return $variable; if(!get_magic_quotes_gpc()) { if(is_array($variable)){ foreach($variable as $k=>$v){ $variable[$k]=cleanQ($v); } return $variable; } //return "'" . mysql_real_escape_string($variable) . "'"; return "'" . addslashes($variable) . "'"; } return addQ($variable); } // $rev 20.05.08 // экранирует спецсимволы внутри переменной вне зависимости от настроек magic_quotes function clean($variable) { if(is_numeric($variable)) return $variable; if(!get_magic_quotes_gpc()) { if(is_array($variable)){ foreach($variable as $k=>$v){ $variable[$k]=clean($v); } return $variable; } //return mysql_real_escape_string($variable); return addslashes($variable); } return $variable; } ?>
апдейт. Как-то мне нужно было обрамить кавычками число. Уже не помню, за каким чертом мне это понадобилось, но было нужно. cleanQ(1, 1); PHP: <?php // $rev 29.06.08 // экранирует спецсимволы внутри переменной вне зависимости от // настроек magic_quotes и обрамляет переменную одинарными кавычками, // даже если она является числом ($bringQ = 1) function cleanQ($var, $bringQ = 0) { // $rev 29.06.08 //обрамляет переменную одинарными кавычками. Видна только из cleanQ if (!function_exists('addQ')) { function addQ($var){ if(is_array($var)){ foreach($var as $k=>$v){ $var[$k]=addQ($v); } return $var; } return "'" . $var . "'"; } } if(is_numeric($var) && !$bringQ) return $var; if(!get_magic_quotes_gpc()) { if(is_array($var)){ foreach($var as $k=>$v){ $var[$k]=cleanQ($v); } return $var; } //return "'" . mysql_real_escape_string($var) . "'"; return "'" . addslashes($var) . "'"; } return addQ($var); } // $rev 20.05.08 // экранирует спецсимволы внутри переменной вне зависимости от настроек magic_quotes function clean($var) { if(is_numeric($var)) return $var; if(!get_magic_quotes_gpc()) { if(is_array($var)){ foreach($var as $k=>$v){ $var[$k]=clean($v); } return $var; } //return mysql_real_escape_string($var); return addslashes($var); } return $var; } ?>