Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Форумы портала PHP.SU :: Версия для печати :: Оптимизация кода.
Форумы портала PHP.SU » Объявления » Наработки по собственным проектам » Оптимизация кода.

Страниц (2): [1] 2 »
 

1. kto-to - 02 Мая, 2011 - 00:44:32 - перейти к сообщению
Здравствуйте!

Возникла необходимость написать движок для одного интернет-проекта, кода-то давно я немного кодил, но опыта, особо, не накопил. Трудность в том, что очень критична скорость работы скриптов и вопрос информационной безопасности.

Общий смысл файла index.php:
1. Определяем, авторизован ли пользователь (получаем права и прочую информацию, которая потом пригодится).
2. Определяем, язык на котором выводим контент (если пользователь авторизован, у него в настройках указан язык).
3. По URI находим контент в БД (получаем тип страницы и прочую информации, которая потом пригодится).
4. Подключаем модуль, соответствующий типу страницы (каталог, галерея, контент, ...).


Если не сложно, посоветуйте, пожалуйста, как оптимизировать код и, возможно, структуру движка.


index.php:
PHP:
скопировать код в буфер обмена
  1.  
  2. <?
  3.  
  4. /******************** VARIABLES ********************/
  5. $db_host = "localhost";
  6. $db_port = "3306";
  7. $db_user = "root";
  8. $db_pass = "";
  9. $db_base = "test";
  10.  
  11. $d_user_data = array("nobody"); // Default user value.
  12. $d_language = "en"; // Default language value.
  13. $d_urn = "main";  // Default URN value.
  14.  
  15. /******************** DB ********************/
  16. $db_link = mysql_connect($db_host.":".$db_port, $db_user, $db_pass) or die(mysql_error());
  17. mysql_select_db($db_base, $db_link) or die(mysql_error());
  18.  
  19. /******************** USER ********************/
  20. $user_data = $d_user_data;
  21. if (isset($_SESSION['user']) and isset($_SESSION['pass'])) {
  22.     $_SESSION['user'] = trim($_SESSION['user']);
  23.     $_SESSION['pass'] = trim($_SESSION['pass']);
  24.     if (strlen($_SESSION['user']) > 0 and strlen($_SESSION['pass']) > 0) {
  25.         $sql = mysql_query("SELECT * FROM `users` WHERE user = `".mysql_escape_string($_SESSION['user'])."' AND pass = '".mysql_escape_string($_SESSION['pass'])."' LIMIT 1") or die(mysql_error());
  26.         if (mysql_num_rows($sql) != 1) {
  27.             $user_data = mysql_fetch_row($sql);
  28.         }
  29.     }
  30. } else if (isset($_POST['user']) and isset($_POST['pass'])) {
  31.     $_POST['user'] = trim($_POST['user']);
  32.     $_POST['pass'] = trim($_POST['pass']);
  33.     if (strlen($_POST['user']) > 0 and strlen($_POST['pass']) > 0) {
  34.         $_SESSION['user'] = $_POST['user'];
  35.         $_SESSION['pass'] = md5($_POST['pass']);
  36.         $sql = ("SELECT * FROM `users` WHERE user = '".mysql_escape_string($_POST['user'])."' AND pass = '".mysql_escape_string(md5($_POST['pass']))."' LIMIT 1") or die(mysql_error());
  37.         if (mysql_num_rows($sql) != 1) {
  38.             $user_data = mysql_fetch_row($sql);
  39.         }
  40.     }
  41. }
  42.  
  43. /******************** LANGUAGE ********************/
  44. $language = $d_language;
  45. if ($user_data != $d_user_data) {
  46.     $language = $user_data[4]; // $user_data[4] = language
  47. } else if (isset($_SESSION['language'])) {
  48.     $_SESSION['language'] = trim($_SESSION['language']);
  49.     if (strlen($_SESSION['language']) == 2) {
  50.         $sql = mysql_query("SELECT * FROM `languages` WHERE language = '".mysql_escape_string($_SESSION['language'])."' LIMIT 1") or die(mysql_error());
  51.         if (mysql_num_rows($sql) != 1) {
  52.             $language = $_SESSION['language'];
  53.         }
  54.     }
  55. }
  56. if (isset($_POST['language'])) {
  57.     $_POST['language'] = trim($_POST['language']);
  58.     if (strlen($_POST['language']) == 2) {
  59.         $sql = mysql_query("SELECT * FROM `languages` WHERE language = '".mysql_escape_string($_POST['language'])."' LIMIT 1") or die(mysql_error());
  60.         if (mysql_num_rows($sql) != 1) {
  61.             $_SESSION['language'] = $language = $_POST['language'];
  62.         }
  63.     }
  64. }
  65.  
  66. /******************** PAGE ********************/
  67. $urn = $d_urn;
  68. if (isset($_GET['urn'])) {
  69.     $_GET['urn'] = trim($_GET['urn']);
  70.     $urn = $_GET['urn'];
  71. }
  72. $sql = mysql_query("SELECT * FROM `pages` WHERE `urn` = '".mysql_escape_string($urn)."' LIMIT 1") or die(mysql_error());
  73. $page_data = mysql_fetch_row($sql);
  74. if (mysql_num_rows($sql) != 1) {
  75.     $urn = $d_urn;
  76.     $sql = mysql_query("SELECT * FROM `pages` WHERE `urn` = '".mysql_escape_string($urn)."' LIMIT 1") or die(mysql_error());
  77.     $page_data = mysql_fetch_row($sql);
  78. }
  79.  
  80. /******************** INCLUDE ********************/
  81. $path = "/home/localhost/subdomain/";
  82. include $path.$page_data[1].".php"; // $page_data[1] = type
  83.  
  84. /******************** DB ********************/
  85. mysql_close($db_link);
  86. ?>
  87.  



Дамп базы:
CODE (SQL):
скопировать код в буфер обмена
  1.  
  2. -- phpMyAdmin SQL Dump
  3. -- version 3.2.3
  4. -- http://www.phpmyadmin.net
  5. --
  6. -- Host: localhost
  7. -- Generation Time: May 02, 2011 at 01:36 AM
  8. -- Server version: 5.1.40
  9. -- PHP Version: 5.3.3
  10.  
  11. SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
  12.  
  13. --
  14. -- Database: `test`
  15. --
  16.  
  17. -- --------------------------------------------------------
  18.  
  19. --
  20. -- Table structure for table `pages`
  21. --
  22.  
  23. CREATE TABLE IF NOT EXISTS `pages` (
  24.   `id` int(11) NOT NULL,
  25.   `type` text COLLATE utf8_unicode_ci NOT NULL,
  26.   `urn` text COLLATE utf8_unicode_ci NOT NULL,
  27.   KEY `id` (`id`)
  28. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  29.  
  30. --
  31. -- Dumping data for table `pages`
  32. --
  33.  
  34. INSERT INTO `pages` (`id`, `type`, `urn`) VALUES
  35. (0, 'catalog', 'main'),
  36. (1, 'page', 'page1');
  37.  
  38. -- --------------------------------------------------------
  39.  
  40. --
  41. -- Table structure for table `users`
  42. --
  43.  
  44. CREATE TABLE IF NOT EXISTS `users` (
  45.   `id` int(11) NOT NULL,
  46.   `user` text COLLATE utf8_unicode_ci NOT NULL,
  47.   `pass` text COLLATE utf8_unicode_ci NOT NULL,
  48.   `language` text COLLATE utf8_unicode_ci NOT NULL,
  49.   KEY `id` (`id`)
  50. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  51.  
  52. --
  53. -- Dumping data for table `users`
  54. --
  55.  
  56. INSERT INTO `users` (`id`, `user`, `pass`, `language`) VALUES
  57. (0, 'nobody', '', 'ru'),
  58. (1, 'alfred', 'dd88f82e4a92f5b61f65c456d544d010', 'en'),
  59. (3, 'vasya', 'a2e2fb92e9ea48f1b26b67983ea729a3', 'ru');
  60.  


Значения для POST переменных, пока, подставлять не пробовал, но, думаю, должно работать без особых ошибок.

P.S.: Что-то мне подсказывает, что получилось ужасно криво...
2. Haron - 02 Мая, 2011 - 07:38:17 - перейти к сообщению
Безопасность:
- Нет контроля того, что приходит от пользователя (GET POST). Поэтому взламывается элементарно. Курите статью про MySQL Injection в википедии. mysql_escape_string - не панацея.
- md5 - легко дешифруется. Есть такая (очень) весёлая программа GPU md5 crack

Структура
- Почитайте про буферизацию вывода. Сильная вещь.
- Не храните короткие строки в полях типа TEXT. Ужасно ресурсоёмкое, и не поддерживающее индексы решение. Храните в VARCHAR.
- Используйте индексы в таблицах. Например повесьте уникальный индекс на поле `urn`.

На этом всё, ниже - чисто рекомендации (Их не обязательно использовать, это моё ИМХО).
- Используйте библиотеки абстракций от БД (Например DBsimple, - сильно экономит время).
- А лучше напишите свою, и желательно под расширение MySQLi.

Смысл в том, чтобы некая абстрагирующая функция (ну например обзовём её query()) - выполняла и
mysql_query, и mysql_fetch_row к примеру, и возвращала уже готовый результат. Принимать она должна строку запроса SQL и параметры.
В неё же (функцию) можно фильтрацию и контроль входящих данных впихнуть.
3. DlTA - 02 Мая, 2011 - 07:42:34 - перейти к сообщению
могу посоветовать вынести подключение к базе 7-19 строчка, отдельным файлом, сократит напряги при переносе и дальнейшей разработке.

еще меня всегда смущало, нафига это делать "$_SESSION['pass'] = trim($_SESSION['pass']);"
если мне нравится в пароли пробелы набивать?!

а с этим: $path = "/home/localhost/subdomain/";
тоже буде куча головняка, можно как то так: include("{$_SERVER['DOCUMENT_ROOT']}/...");
4. Champion - 02 Мая, 2011 - 09:04:41 - перейти к сообщению
kto-to пишет:
/******************** VARIABLES ********************/
Не надо так делать. Это мусорный комментарий. Если хотетите выделить смысловой блок - оформите его в функцию или (всякие конфиги) вынесите в отдельный файл.

Haron,DlTA - всё по делу, но
Haron пишет:
md5 - легко дешифруется.
Немного не так. md5 вообще не дешифруется. Никак. Атака с использованием радужной таблицы - это не дешифрация, а по сути просто очень эффективная модификация брута. И сломать позволяет далеко не всё. А использование соли делает эти таблицы вообще неэффективными.
kto-to хешировать лучше не пароль, а md5(чудо-строка), где чудо-строка получается путем конкатенации пароля со случайным образом сгенерированной еще одной строкой - солью.
Haron пишет:
Почитайте про буферизацию вывода. Сильная вещь.
Обычная вещь. Да и к чему она тут?
Haron пишет:
Храните в VARCHAR.
а поля type и language можно даже в enum.
5. Haron - 02 Мая, 2011 - 10:55:35 - перейти к сообщению
Champion пишет:
Немного не так. md5 вообще не дешифруется. Никак.

Я бы не был столь категоричен: http://eprint[dot]iacr[dot]org/2004/199[dot]pdf
Брутфорс же md5 - сегодня делается в обозримые сроки (Используется CUDA). Неважно, есть соль, или её нетЪ.

Champion пишет:
Обычная вещь. Да и к чему она тут?

К тому, что с буферизацией страница будет отдаваться уже готовой - сразу и вся, а не рисоваться постепенно (порой это очень некрасиво смотрится). А если ещё и закешировать (например в файлы) - +100500 к быстродействию.
6. Champion - 02 Мая, 2011 - 11:10:57 - перейти к сообщению
Я тут вообще не увидел ничего похожего на вывод, поэтому не понял, зачем здесь заводить речь о буферизации)
Я вот могу с таким же успехом посоветовать совершать пробежки в парке - это тоже полезная вещь)
Haron пишет:
http://eprint[dot]iacr[dot]org/2004/199[dot]pdf
Брутфорс же md5 - сегодня делается в обозримые сроки (Используется CUDA). Неважно, есть соль, или её нетЪ.
Ну это не дешифрация. Понятное дело, что любое хэш значение имеет бесконечное число коллизий. Одно дело подобрать одну из коллизий, другое - реальное захешированное значение. Хотя часто можно воспользоваться одним вместо другого.
По поводу CUDA - я думаю, не каждый злоумышленник может ей воспользоваться, тем более для атаки на творчетсво kto-to. Ничего плохого не хочу сказать про kto-to, но думаю, что его сайт не предстваляет интереса для злобных хакеров - только для энтузиастов. Так что соли вполне достаточно.
Кстати, я увидел камень, брошенный в md5, но не увидел ничего предложенного взамен.
7. Мелкий - 02 Мая, 2011 - 11:21:19 - перейти к сообщению
Haron пишет:
Брутфорс же md5 - сегодня делается в обозримые сроки (Используется CUDA). Неважно, есть соль, или её нетЪ.

Угу, ещё одна жертва маркетинга, как и я в своё время. Вот вам задание:
KeyCAPTCHA пишет:
скажите пароль для "подписи" вот этих двух строк:

строка1: http:/www[dot]yandex[dot]ru/webmaster|GGGDDDBB[dot][dot][dot]890223344|PRIVET
хэш1: 68B467EE6625898B2B0BF7CFC7B8E6C7

строка2: http:/www[dot]yandex[dot]ru/top|GGGDDDBBB7890223344|100BAKSOV
хэш2: FD9BBA0F534859764BC16BFBD0F493F1

пароль прибавляется к началу строк то есть хэш = MD5( пароль + строка ). Пароль ОДИНАКОВЫЙ для обоих строк.

мой пароль содержит от 20 до 30 символов и имеет только анлгийские большие и маленькие буквы (вобщем длинной около 150 бит) ну это чтобы вам было проще
8. kto-to - 02 Мая, 2011 - 19:06:12 - перейти к сообщению
Haron, DlTA, Champion, Мелкий, благодарю за ответы!

1. А что mysql_escape_string() пропустит? По идее, БД должна уметь обрабатывать любые данные, просто их надо предоставить в нужном виде, или я чего-то не так понимаю? Как там SQL-inj провести?
2. Слышал, что md5 лучше использовать с солью. Переделаю. А, на самом деле, какая реальная альтернатива md5?
3. Для буферизации, пока, рано. Тут вывода еще нет. Но обязательно учту, когда буду писать вывод.
4. Да, со структурой БД у меня беда. Переделаю.
5. Подключение к БД сознательно не стал выносить в отдельный файл, потому, что include() замедлит скорость работы скрипта.
6. Оправдано ли использование trim()... Думаю, что стоит оставить, потому, что пробелы по краям редко ставятся сознательно. Хотя... Лишняя функция - лишнее время.
7. $_SERVER['DOCUMENT_ROOT'] - это да. Переделаю.
8. По поводу комментариев уже сломал себе голову... Выносить блоки в функции - увеличить время выполнения сценария, а совсем без комментариев код слабо читабельный.
9. Хотелось бы, чтоб обошлось без этого, но хакерские атаки (не знаю, на сколько профессиональные, но заказные) скорее всего будут.

Да, по поводу библиотеки абстракций от БД... Опять же, она облегчает процесс написания, но замедляет выполнение сценариев. Да и приложения сторонних разработчиков, без крайней необходимости, я использовать не хочу.
9. Мелкий - 02 Мая, 2011 - 19:32:02 - перейти к сообщению
1. тоже любопытно. Для базы данных mysql_escape_string вполне достаточно.
2. sha1 есть ещё.
5 & 6 & 8. Это называется "преждевременная оптимизация". Забейте на наносекунды, для них всё равно PHP не подходит и делайте так, чтобы это было удобно.
10. kto-to - 02 Мая, 2011 - 20:16:38 - перейти к сообщению
2 Мелкий

О sha1 прочитал. Я так понимаю, что принцип, по сути, такой же, как и у md5. Существенной разницы между ними, я найти не могу. Единственное, возможно использовать их совместно. Или я ошибаюсь?

Преждевременная оптимизация, возможно. Но нет ничего более постоянного, чем что-либо временное. Не хочется в итоге получить две килотонны медленного кода, который надо везде подправить. Возможно php не самый быстрый язык, но именно он мне в данной ситуации позволит относительно быстро добиться цели. И именно от него я сейчас намерен получить максимальную скорость.
11. Haron - 02 Мая, 2011 - 21:06:48 - перейти к сообщению
kto-to пишет:
Единственное, возможно использовать их совместно. Или я ошибаюсь?

Совместно - да, хорошая идея

kto-to пишет:
Преждевременная оптимизация, возможно. Но нет ничего более постоянного, чем что-либо временное.

И по поводу абстракций...
Пример - три функции делающих что либо (На входе $var - на выходе $c)

$a = func1($var);
$b = func2($a);
$c = func3($b);

Как думаете, что быстрее и читабельней?
Использование подобных повторяющихся конструкций в коде 100500 раз?
Или единственная конструкция вынесенная в функцию? (Необязательно в отдельный файл)?
12. Champion - 02 Мая, 2011 - 21:13:24 - перейти к сообщению
Haron пишет:
Совместно - да, хорошая идея
Плохая идея. Похоже на панику. Шифровать всем чем можно. md5 + соль и всё будет здорово.
kto-to пишет:
По поводу комментариев уже сломал себе голову... Выносить блоки в функции - увеличить время выполнения сценария, а совсем без комментариев код слабо читабельный.
Правильно сказали - это преждевременная оптимизация. Такой код гораздо сложнее сопровождать, в нем проще ошибиться - и все ради выйгрыша времени, который толком и засечь-то не удастся.
13. OrmaJever - 02 Мая, 2011 - 21:20:57 - перейти к сообщению
Нащёт шифровки, чистая md5 очень устаревает т.к. уже много сервисов по подборке, с сольюя думаю тоже могут распаковать. Я раньше выдрал функцию из phpbb3, она похожа на md5 но мало известна
PHP:
скопировать код в буфер обмена
  1. function _hash_encode64($input, $count)
  2. {
  3.         $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  4.               $output = '';
  5.         $i = 0;
  6.  
  7.         do
  8.         {
  9.                 $value = ord($input[$i++]);
  10.                 $output .= $itoa64[$value & 0x3f];
  11.  
  12.                 if ($i < $count)
  13.                 {
  14.                         $value |= ord($input[$i]) << 8;
  15.                 }
  16.  
  17.                 $output .= $itoa64[($value >> 6) & 0x3f];
  18.  
  19.                 if ($i++ >= $count)
  20.                 {
  21.                         break;
  22.                 }
  23.  
  24.                 if ($i < $count)
  25.                 {
  26.                         $value |= ord($input[$i]) << 16;
  27.                 }
  28.  
  29.                 $output .= $itoa64[($value >> 12) & 0x3f];
  30.  
  31.                 if ($i++ >= $count)
  32.                 {
  33.                         break;
  34.                 }
  35.  
  36.                 $output .= $itoa64[($value >> 18) & 0x3f];
  37.         }
  38.         while ($i < $count);
  39.  
  40.         return $output;
  41. }
14. EuGen - 02 Мая, 2011 - 21:23:07 - перейти к сообщению
Для БД используйте готовые адаптеры (чтобы не писать свои). Zend к примеру.
Остальное можно вынести в модели - создав соответствующие классы.
Объектный подход почти всегда сопутствует масштибаруемости и гибкости - конечно, если применен верно.
Почитайте также про паттерны проектирования.
15. Мелкий - 02 Мая, 2011 - 21:58:17 - перейти к сообщению
kto-to пишет:
Не хочется в итоге получить две килотонны медленного кода, который надо везде подправить.

Именно к этому вы и идёте. В зависимости от личных способностей, килобайтах на 20++ кода начнёте нещадно путаться, потеряете контроль над кодом и придёте к решению, что всё это надо выбросить и писать нормально - так, чтобы было удобно разработчику, ведь его время куда дороже машинного.

 

Powered by ExBB FM 1.0 RC1