За последние 24 часа нас посетили 22640 программистов и 1281 робот. Сейчас ищут 772 программиста ...

Вопросы безопасности, или «почему так делать нельзя»

Тема в разделе "PHP для новичков", создана пользователем Anonymous, 27 сен 2007.

Статус темы:
Закрыта.
  1. Anonymous

    Anonymous Guest

    Данная тема предназначена для новичков, здесь будут описаны «нежелательные» примеры программирования, настроек, нарушений безопасности приложения, и правильные пути решения их.
    Модерируется строго!
    Оставляйте свои вопросы, так же сюда будут переноситься часть обсуждений из других тем.
     
  2. Anonymous

    Anonymous Guest

    PHP:
    1. <?php
    2.  if(@$_GET['id']=='1')
    3.      setcookie("tarif",'test1',time()+3600);
    4.  if(@$_GET['id']=='2')
    5.      setcookie("phone",'test2',time()+3600);
    6.  if(@$_GET['id']=='3')
    7.      setcookie("nomer",'test3',time()+3600);
    8.  ?>
    9.  
    10.  <a href="index.php?id=1">1</a><br />
    11.  <a href="index.php?id=2">2</a><br />
    12.  <a href="index.php?id=3">3</a><br />
    13.  
    14.  <?php
    15.  echo '<pre>';
    16.  @print_r($_COOKIE);
    17.  echo '</pre>';
    18.  ?>
    В приведенном примере плохой практикой является использование GET параметров без их проверки на допустимость, а так же подавление ошибок вывода.
     
  3. S.t.A.M.

    S.t.A.M. Активный пользователь

    С нами с:
    10 сен 2007
    Сообщения:
    1.041
    Симпатии:
    0
    Горбунов Олег, добавь. что для проверки можно использовать связку isset и empty
     
  4. Luge

    Luge Старожил

    С нами с:
    2 фев 2007
    Сообщения:
    4.680
    Симпатии:
    1
    Адрес:
    Минск
    PHP:
    1. <?php
    2. if(isset($_GET['page']) && !empty($_GET['page']))
    3. include(trim($_GET['page']));
    4. ?>
    Если в конфиге php разрешено инклудить файл с других серверов, то данный подход позволяет подключить скрипт с другого сервера (например http://mysite.com/?page=http://anothers ... script.php)

    Решение от Vladson'a
    PHP:
    1. <?php
    2. $mode = 'default';
    3. if ( isset($_GET['mode']) ) {
    4.    if ( !empty($_GET['mode']) ) {
    5.       if ( !preg_match("#[^a-z]#s", $_GET['mode']) ) {
    6.          $mode = $_GET['mode'];
    7.       }
    8.    }
    9. }
    10. $filename = 'modules/module_' . $mode . '.php';
    11. if ( file_exists($filename) ) {
    12.    include $filename;
    13. } else {
    14.    include 'includes/error.php';
    15. }
    16. ?>
     
  5. Diver

    Diver Активный пользователь

    С нами с:
    19 фев 2006
    Сообщения:
    144
    Симпатии:
    0
    Адрес:
    Владивосток
    PHP:
    1.  
    2. <?
    3. $mode = isset($_GET['mode']) ? trim($_GET['mode']) : FALSE;
    4. if ($mode)
    5. {
    6.    switch $mode;
    7.    {
    8.        case "1" : include "1.php";
    9.        break;
    10.        ...
    11.     }
    12. }
    13. else
    14. {
    15.    include "default.php";
    16. }
    17. ?>
    18.  
     
  6. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    Можно и без условия:
    PHP:
    1.  
    2. <?php
    3. switch ($mode) {
    4.     case '1': include '1.php'; break;
    5.     // ...
    6.     case 'n': include 'n.php'; break;
    7.     default: include 'default.php'; break;
    8. }
    Тогда за загрузку модуля по умолчанию будет отвечать default:
     
  7. vb

    vb Активный пользователь

    С нами с:
    6 июн 2006
    Сообщения:
    911
    Симпатии:
    0
    Адрес:
    Saint-Petersburg
    PHP:
    1.  
    2. <?php
    3. // Дано
    4. // mysql.table_categories = 100 items; таблица категорий товаров = 100 записей
    5. // mysql.table_products = 1000 items; таблица товаров = 1000 записей
    6. // Нужно вывести 1000 товаров с наименованием категорий.
    7.  
    8. $time = array (getmicrotime());
    9. //========================
    10. // Метод 1.
    11. //========================
    12. $result = mysql_query("SELECT category_id, name FROM table_products LIMIT 0, 1000");
    13. while ($row = mysql_fetch_assoc($result))
    14. {
    15.     $result2 = mysql_query("SELECT name FROM table_categories WHERE id='{$row['category_id']}' LIMIT 1");
    16.     if (list($category) = mysql_fetch_row($result2))
    17.     {
    18.         //...
    19.         //print "category = {$category}, product = {$row['name']}<br />\n";
    20.     }
    21. }
    22.  
    23. $time['1: query of while of query'] = getmicrotime();
    24. //========================
    25. // Метод 2.
    26. //========================
    27. $categories = array ();
    28. $result = mysql_query("SELECT id, name FROM table_categories");
    29. while ($row = mysql_fetch_assoc($result))
    30. {
    31.     $categories [$row['id']] = $row['name'];
    32. }
    33. $result = mysql_query("SELECT category_id, name FROM table_products LIMIT 0, 1000");
    34. while ($row = mysql_fetch_assoc($result))
    35. {
    36.     //...
    37.     //print "category = {$categories[$row['category_id']]}, product = {$row['name']}<br />\n";
    38. }
    39.  
    40. $time['2: two query and one array'] = getmicrotime();
    41. //========================
    42. // Метод 3.
    43. //========================
    44. $result = mysql_query("SELECT p.name, c.name as category FROM table_products as p,
    45.                         table_categories as c WHERE c.id=p.category_id GROUP BY p.id LIMIT 0, 1000");
    46. while ($row = mysql_fetch_assoc($result))
    47. {
    48.     //...
    49.     //print "category = {$row['category']}, product = {$row['name']}<br />\n";
    50. }
    51.  
    52. $time['3: one query with two table'] = getmicrotime();
    53. //========================
    54. // Метод 4.
    55. //========================
    56. $result = mysql_query("SELECT p.name, c.name as category FROM table_products as p
    57.                         LEFT JOIN table_categories as c ON c.id=p.category_id GROUP BY p.id LIMIT 0, 1000");
    58. while ($row = mysql_fetch_assoc($result))
    59. {
    60.     //...
    61.     //print "category = {$row['category']}, product = {$row['name']}<br />\n";
    62. }
    63.  
    64. $time['4: one query with LEFT JOIN'] = getmicrotime();
    65. //========================
    66. $last_time = null;
    67. foreach ($time as $label=>$value)
    68. {
    69.     if (empty($last_time)) $last_time = $value;
    70.     else
    71.     {
    72.         print "{$label} == ".($value - $last_time)." s. <br />\n";
    73.         $last_time = $value;
    74.     }
    75. }
    76.  
    77. function getmicrotime()
    78. {
    79.     list($usec, $sec) = explode(" ", microtime());
    80.     return ((float)$usec + (float)$sec);
    81. }
    82. ?>
    83.  
    Результат:
    Код (Text):
    1.  
    2. 1: query of while of query == 0,096814155578613 s.
    3. 2: two query and one array == 0,010401964187622 s.
    4. 3: one query with two table == 0,0089080333709717 s.
    5. 4: one query with LEFT JOIN == 0,0088870525360107 s.
    Для недоверчивых тестовые данные:
    [sql]
    CREATE TABLE `table_categories` (
    `id` int(11) NOT NULL auto_increment,
    `name` varchar(255) default NULL,
    PRIMARY KEY (`id`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
    CREATE TABLE `table_products` (
    `id` int(11) NOT NULL auto_increment,
    `category_id` int(11) NOT NULL default '0',
    `name` varchar(255) default NULL,
    PRIMARY KEY (`id`),
    KEY `category_id` (`category_id`)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
    [/sql]
    Заполнение таблиц:
    PHP:
    1. <?php
    2. for ($i = 1; $i <= 100; $i++)
    3. {
    4.     mysql_query('INSERT table_categories VALUES(null, '.$i.')');
    5.     for ($j = 1; $j < 10; $j++)
    6.     {
    7.         mysql_query('INSERT table_products VALUES(null, '.$i.', '.($i*100+$j).')');
    8.     }
    9. }
    10. ?>
     
  8. host

    host Активный пользователь

    С нами с:
    20 июн 2007
    Сообщения:
    733
    Симпатии:
    3
    Как не стоит делать:

    PHP:
    1. <?php
    2. mysql_connect("localhost", "root", "");
    3. $var = mysql_query("SELECT * FROM my_table WHERE status='On' AND selected='y'");
    4. $var1 = mysql_fetch_array($var);
    5.  
    6. for($i=0; $i<mysql_num_rows($var); $i++)
    7. {
    8. echo $var1['id'];
    9.  
    10. // и т.д.
    11. }
    12. ?>
    или
    PHP:
    1. <?php
    2. mysql_connect("localhost", "root", "");
    3.  
    4. $name = array("Вася", "Дима", "Саша", "Сережа", "Олег");
    5.  
    6. for($i=0; $i<5;$i++)
    7. {
    8.  
    9. $var = mysql_query("SELECT * FROM my_table WHERE name=\"{$name[$i]}\"");
    10. $var1 = mysql_fetch_array($var);
    11.  
    12. echo $var1['phone'];
    13.  
    14. }
    15. ?>
     
  9. Luge

    Luge Старожил

    С нами с:
    2 фев 2007
    Сообщения:
    4.680
    Симпатии:
    1
    Адрес:
    Минск
    на правах оффтопа котрый всё равно удалит Горбунов Олег.
    host, перепутал переменные в первом примере и просчитался с количеством проходов по массиву.
    Сам придумал? ;) А то таких извратов имхо и в блондинках не пишут уже
     
  10. host

    host Активный пользователь

    С нами с:
    20 июн 2007
    Сообщения:
    733
    Симпатии:
    3
    // offtop
    Уставший за сегодня полностью. Да и вечер уже. Так что извиняйте.
    PS Исправлено.
    PPS Такие примеры я действительно видел, поэтому и написал ;)
     
  11. S.t.A.M.

    S.t.A.M. Активный пользователь

    С нами с:
    10 сен 2007
    Сообщения:
    1.041
    Симпатии:
    0
    host, это наверно очень важно, но совершенно бесполезно! т.к. я не понял что именно делать не стоит: соединятся таким образом с базой? Выводить так массив? Кодить без отступов? использовать "*" когда нужен только id?
    Требуются пояснения, или, что еще лучше, пример как это делать НАДО! И конечно же хочется понять почему? т.е. чем это чревато.
    Заранее спасибо.

    ЗЫ я делаю именно так (только использую функцию для обработки запроса и foreach вместо for)
     
  12. vb

    vb Активный пользователь

    С нами с:
    6 июн 2006
    Сообщения:
    911
    Симпатии:
    0
    Адрес:
    Saint-Petersburg
    PHP:
    1. <?php
    2. //Скрипту передается через $_GET или $_POST параметр по которому нужно сделать выборку в базе.
    3. $result = mysql_query ("SELECT * FROM table_name WHERE id='{$_GET['id']}'");
    4. //например $_GET['id'] == "' UNION /*...на что фантазии хватит...*/ '"
    5. //получется запрос:
    6. //SELECT * FROM table_name WHERE id='' UNION /*...на что фантазии хватит...*/ '' //'' - это два апострофа а не кавычка
    7.  
    8. //как надо было:
    9. //если известно, что id - целое число
    10. $id = (int) $_GET['id'];
    11. //если известно что это набор букв и/или цифр
    12. $id = mysql_escape_string($_GET['id']);
    13. //ну и сам запрос
    14. $result = mysql_query ("SELECT * FROM table_name WHERE id='{$id}'");
    15. /*
    16. * Если в вышем запросе используется LIKE,
    17. * например, SELECT * FROM table_name WHERE login LIKE '$login'
    18. * этот запрос выполнит поиск логина без учета регистра
    19. * помимо вышеописанных обработок нужно сделать дополнительную
    20. * LIKE воспринимает "_" как любой символ, а "%" как любые символы
    21. * соответственно надо их экранировать
    22. */
    23. $login = str_replace (array ('%', '_'), array ('\%', '\_'), $login);
    24. ?>
     
  13. Vladson

    Vladson Старожил

    С нами с:
    4 фев 2006
    Сообщения:
    4.040
    Симпатии:
    26
    Адрес:
    Estonia, Tallinn
    Я бы предложил для подготовки запросов юзать sprintf()

    Т.е
    PHP:
    1. <?php
    2. // Подготовка запроса
    3. $sql = sprintf("SELECT * FROM `table_name` WHERE `id`=%d AND `name`='%s'",
    4.     intval($_GET['id']), // вот тут в принципе intval не нужен, просто привычка
    5.     mysql_escape_string($_GET['name'])
    6. );
    7. // сам запрос
    8. $result = mysql_query ($sql);
    9. ?>
     
  14. Битник

    Битник Активный пользователь

    С нами с:
    9 фев 2007
    Сообщения:
    103
    Симпатии:
    0
    Объяснил бы что ли, что и почему не стоит делать.
     
  15. host

    host Активный пользователь

    С нами с:
    20 июн 2007
    Сообщения:
    733
    Симпатии:
    3
    S.t.A.M., Битник

    В первом случае не стоит использовать mysql_num_rows в цикле, т.е.
    лучше
    PHP:
    1. <?php
    2. $rows = mysql_num_rows($var);
    3. for($i=0; $i<$rows; $i++)
    4. ?>
    а еще лучше
    PHP:
    1. <?php
    2. while($var1 = mysql_fetch_array($var))
    3. {
    4. echo $var1['id'];
    5. }
    6. ?>
    Во втором:
    Лучше не использовать цикл for с запросом к БД.
    Лучше использование foreach и не стоит злоупотреблять запросами в циклах.

    В обоих вариантах:
    Использование логина root и без пароля.
     
  16. Luge

    Luge Старожил

    С нами с:
    2 фев 2007
    Сообщения:
    4.680
    Симпатии:
    1
    Адрес:
    Минск
    Битник
    тут скоре нерациональность, нежели опасный подход
     
  17. host

    host Активный пользователь

    С нами с:
    20 июн 2007
    Сообщения:
    733
    Симпатии:
    3
    именно
     
  18. DarkElf

    DarkElf Активный пользователь

    С нами с:
    22 окт 2006
    Сообщения:
    1.632
    Симпатии:
    0
    host

    на самом деле все зависит от характеристик массива. если там идет последовательность нумерованных значений, и нумерация непрерывна, и число элементов массива от 100, то while, по моим наблюдениям, работает быстрее for, не говоря о foreach. имхо, удел foreach - это ассоциированные массивы.
     
  19. host

    host Активный пользователь

    С нами с:
    20 июн 2007
    Сообщения:
    733
    Симпатии:
    3
    DarkElf
     
  20. Ruzzz

    Ruzzz Активный пользователь

    С нами с:
    11 фев 2008
    Сообщения:
    148
    Симпатии:
    1
    Я новичок в PHP. Хотел бы больше услышать в ветке о безопасности, а не о скорости. Насколько я понял с безопасностью связано проверка переданных параметров. Параметры могут интерпретироватся как "команды" если мы используем их в SQL-запросе или если мы будем использовать значения этих параметров при выдаче браузеру. Можете толково разжувать в кратце все основные моменты?

    Ниже то что я для себя понял )
    PHP:
    1.    
    2. <?php
    3.     // Проверка того что параметр передан и не пуст
    4.     if (isset($_GET['param']) && !empty($_GET['param'])) { };
    5.  
    6.     // Проверка с помощью регулярного выражения      
    7.     // Очень мощьное средство!
    8.     if ( !preg_match("#[^a-z]#s", $_GET['param']) ) { }                                          
    9.    
    10.     // Если известен тип, то делать явное приведение типов. (пример для int)
    11.     $param = (int) $_GET['param'];  
    12.    
    13.     // Преобразовывать "опасные" символы, перед использованием строки в sql-запросе
    14.     // Смотреть также htmlentities и htmlspecialchars
    15.     $param = mysql_escape_string($_GET['param']);
    16.                                    
    17.     // Потому как LIKE воспринимает "_" как любой символ,
    18.     // а "%" как любые символы, при использовании LIKE в sql-запросе
    19.     // например SELECT * FROM table_name WHERE login LIKE '$login'
    20.     // следует:
    21.     $param = str_replace (array ('%', '_'), array ('\%', '\_'), $param);
    22.  
    23.     // Использование следующей конструкции позволит
    24.     // Отсеят "нежелательные" значения параметра
    25.     $mode = $_GET['mode'];
    26.     switch ($mode) {
    27.         case '1': /* Что-то делаем */ ; break;
    28.         // ...
    29.         case 'n': /* Что-то делаем */ ; break;
    30.         default:  /* Что-то делаем */ ; break;
    31.     }
    32. ?>
    33.  
    Есть какое-то упоминание об intval($_GET['id']). Что это я не знаю (
    Еще слышал об использовании substr($x,0,...); ? Это как? Типа брать не больше n символов?

    Дополните позжлста!
     
  21. armadillo

    armadillo Активный пользователь

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    да.
    http://php.net/intval
     
  22. Ruzzz

    Ruzzz Активный пользователь

    С нами с:
    11 фев 2008
    Сообщения:
    148
    Симпатии:
    1
    В чем отличие $param = intval($_GET['param']) и $param = (int) $_GET['param'];
     
  23. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    Первое - вызов функции, второе - приведение типов.
     
  24. Ruzzz

    Ruzzz Активный пользователь

    С нами с:
    11 фев 2008
    Сообщения:
    148
    Симпатии:
    1
    Обьясните позлста, что лучше использовать intval или (int)?
     
  25. Vladson

    Vladson Старожил

    С нами с:
    4 фев 2006
    Сообщения:
    4.040
    Симпатии:
    26
    Адрес:
    Estonia, Tallinn
    Лучше ни то ни другое, просто делать preg_match на наличие в строке чего либо кроме цифр, и в зависимости от этого выдавать ошибку или работать дальше :D
    (тормознутее, но за то не вмешиваясь в типы)
     
Статус темы:
Закрыта.