webew
Войти » Регистрация
 
PHP :: безопасность

Особенности условных конструкции со строками в PHP

1 июня 2008, 16:09

Операции сравнения, операторы if и switch могут вести себя неожиданно в присутствии строковых переменных. Незнание поведения данных операторов может приводить к ошибкам безопасности приложения.

Что есть истина?

Какие строки истинны? Рассмотрим пример:

echo '"" is ' . (''?'true':'false') . "\n";
echo '"0" is ' . ('0'?'true':'false') . "\n";
echo '"-0" is ' . ('-0'?'true':'false') . "\n";
echo '"0.0" is ' . ('0.0'?'true':'false') . "\n";
echo '"00" is ' . ('00'?'true':'false') . "\n";
echo '"A" is ' . ('A'?'true':'false') . "\n";

Имеем:

"" is false
"0" is false
"-0" is true
"0.0" is true
"00" is true
"A" is true

Итак истинны все непустые строки, кроме строки "0". Логика такого поведения для меня не ясна. На практике часто с помощью оператора if проверяется заполнено ли поле формы, например:

if(isset($_GET['income']) && $_GET['income']) { .. }

Такая проверка примет поле, в котором указан 0 за незаполненное. Чтобы избежать возможного недоразумения, следует использовать проверку в виде:

if(isset($_GET['income']) && strlen($_GET['income'])) { .. }

Сравнение с участием строк

В PHP два оператора сравнения == и ===. Второй оператор сравнивает совпадение типов и значений, первый производит преобразование типа, а затем выполняет сравнение. Особенность оператора == в том, что при сравнении числа и строки, просходит преобразование строки в число. Если строка не представляет собой число, то она преобразуется в числовое значение 0. Рассмотрим пример:

echo '"A"==0 is ' . ('A'==0?'true':'false') . "\n";
echo '"A"===0 is ' . ('A'===0?'true':'false') . "\n";
echo '"A"==0.0 is ' . ('A'==0?'true':'false') . "\n";
echo '"A"===0.0 is ' . ('A'===0?'true':'false') . "\n";
Результат работы скрипта:
"A"==0 is true
"A"===0 is false
"A"==0.0 is true
"A"===0.0 is false

Если ваша функция сравнивает строку, переданную в качестве аргумента с заданной строкой, то следует предварительно проверить, что в качестве аргумента передана строка, а не число. Пример будет приведен далее, при рассмотрении оператора switch.

Оператор switch

Мы ожидаем, что оператор switch сравнивает переданное значение с заданными константами. Особенность реализации switch в php в том, что он производит сравнение с помощью оператора ==, то есть не производит сравнение типов. В качестве примера рассмотрим вымышленную функцию, которая возвращает зарплату сотрудника, получая его имя на входе:

<?php
function get_salary($name) {
  switch($name) {
   case 'John':
     return 3400;
     break;
   case 'Mary':
     return 4600;
     break;
   default:
     return 0;
  }
}

echo "John's salary is " . get_salary('John') . "\n";
echo "Mary's salary is " . get_salary('Mary') . "\n";
echo "Peter's salary is " . get_salary('Peter') . "\n";
echo "0's salary is " . get_salary(0) . "\n";
?>
Результат работы скрипта:
John's salary is 3400
Mary's salary is 4600
Peter's salary is 0
0's salary is 3400

На примере видно, что безопасность функции скомпрометирована путем передачи числового значения вместо строкового. Чтобы защитить функцию от нежелательного использования, требуется проверять тип входного значения.

<?php
function get_salary($name) {
  if(!is_string($name)) return 0;
  switch($name) {
   case 'John':
     return 3400;
     break;
   case 'Mary':
     return 4600;
     break;
   default:
     return 0;
  }
}

echo "John's salary is " . get_salary('John') . "\n";
echo "Mary's salary is " . get_salary('Mary') . "\n";
echo "Peter's salary is " . get_salary('Peter') . "\n";
echo "0's salary is " . get_salary(0) . "\n";
?>
Результат работы скрипта:
John's salary is 3400
Mary's salary is 4600
Peter's salary is 0
0's salary is 0

Итак, некоторые операции сравнения ведут себя контринтуитивно. Кто предупрежден, тот вооружен.

Статья написана по материалам онлайн-курса «Программирование на PHP».


© Все права на данную статью принадлежат порталу webew.ru. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
NO USERPIC

Sign

>Итак истинны все непустые строки, кроме строки "0". Логика такого поведения для меня не ясна.
На самом деле тут всё логично...
Обычно известен ожидаемый тип данных, попробуйте его использовать:
$digit = isset($_GET['digit']) ? intval($_GET['digit']) : 0;
$str = isset($_GET['str']) ? trim($_GET['str']) : '';
// или так:
if(!isset($_GET['str']) or !($str = trim($_GET['str'])) {
  ...
}
27.06.2008, 08:16
Ответить
NO USERPIC

rgbeast

> На самом деле тут всё логично...

Поясните пожалуйста логику. В Вашем примере используется trim - функция, удаляющая пробелы из начала и конца строки. Мой аргумент заключается в том, что если в строке (тип известен - строка) содержится значение "0", то такая строка считается ложной, в то же время строка "00" считается истинной. На мой взгляд такое поведение малоюзабельно.
27.06.2008, 09:41
Ответить
NO USERPIC

Sign

Логичность, как мне кажется в чёткости немногочисленных правил приведения типов. При большем количестве правил мы имели бы следующий ад:
(bool)'00' is false
(bool)'0x00' is false
(bool)'false' is false
(bool)'null' is false
и будучи последовательными пришли бы к необходимости при приведении строки к другим типам выполнять что-то вроде eval() для сравниваемой строки вместо одного простого сравнения.
27.06.2008, 13:06
Ответить
NO USERPIC

rgbeast

Для строк можно было бы упростить правила - считать истинной любую непустую строку. В PHP введено исключение - строка "0", что кажется усложнением.
27.06.2008, 21:45
Ответить
NO USERPIC

Sign

Простите, не сразу уловил Вашу мысль..
Возможно это упрощение привело бы к ещё большим сложностям, так как при
$row = mysql_fetch_row(mysql_query('select 1-1'));
print_r($row);
echo serialize($row);

получаем строку:
Array ( [0] => 0 )
a:1:{i:0;s:1:"0";}
30.06.2008, 10:22
Ответить
NO USERPIC

rgbeast

Удивляет то, что 'SELECT 1.0-1.0' сработает иначе в указанном Вами примере. Получится '0.0', что уже true
30.06.2008, 11:03
Ответить
NO USERPIC

Sign

Согласен, нет счастья в этом мире ; )
Хотя на самом деле я имел ввиду запросы включающие COUNT, возвращающие зачастую ноль
30.06.2008, 11:44
Ответить
NO USERPIC

AlexNZ

"0" is true - это усложнения для разработчиков, пишущих на PHP, а вот для авторов PHP это упрощение. Автоматическое приведение типа произойдет раньше сравнения, таков движок. Для изменения поведения авторам PHP пришлось бы приложить дополнительные усилия, но гораздо проще документировать это как особенное правило и после гордиться. На мой взгляд корень проблемы таков.
06.08.2008, 09:41
Ответить
NO USERPIC

rgbeast

Надо смотреть исходники PHP. Все-таки любое приведение типов строку "00" тоже приведет к нулю, однако эта строка есть true. Правильно говорят классики, что дизайн и реализация должны разделяться. Их конечно никто не слушает (и на это есть объективные причины, в виде удорожания разработки), поэтому имеем массовое документирование багов.
06.08.2008, 12:00
Ответить
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
© 2008—2017 webew.ru, связаться: x собака webew.ru
Сайт использует Flede и соответствует стандартам WAI-WCAG 1.0 на уровне A.
Rambler's Top100

Реклама: