mysql_real_escape_string() не работает

Вурдалак

Продвинутый новичок
mysql_real_escape_string() не работает

PHP:
<?php

error_reporting(E_ALL | E_STRICT);

set_error_handler(function($errno, $errstr, $errfile = '', $errline = 0, array $errcontext = NULL) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

try {
    $charset = 'big5'; // Chinese encoding
    
    header('Content-type: text/plain; charset=' . $charset);

    mysql_connect('localhost', 'root', 'qwerty');
    mysql_select_db('test_db');

    if( ! mysql_query('SET NAMES ' . $charset) ) {
        die('Error while executing query: ' . mysql_error());
    }
    
    $name = "\xA3', 0x1234) -- ";
    $name_lit = "'" . mysql_real_escape_string($name) . "'";

    $query = "INSERT INTO tbl (name, img) VALUES ($name_lit, '')";

    if( ! mysql_query($query) ) {
        die('Error while executing query "' . $query . '": ' . mysql_error());
    }
} catch(ErrorException $e) {
    echo $e->getMessage();
}
[sql]CREATE TABLE `tbl` (
`id` INT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR( 100 ) CHARACTER SET big5 COLLATE big5_chinese_ci NOT NULL ,
`img` BLOB NOT NULL ,
PRIMARY KEY ( `id` )
);[/sql]

PHP version: 5.3.1
MySQL version: 5.1.40-community

Что за херня, собственно? Я получаю то, что как раз быть не должно: SQL injection. Запрос получается таким:
[sql]INSERT INTO tbl (name, img) VALUES ('&#945;', 0x1234) -- ', '')[/sql] (вместо &amp;#945; буква «альфа»).

Аналогично не работает и с GBK, т.е. mysql_escape_string($name) === mysql_real_escape_string($name). Скорее всего mysql_real_escape_string() не получает кодировку текущего соединения. Почему?

P.S. Кстати, с каких пор требуется завершение комментария «*/»?
 

tz-lom

Продвинутый новичок
честно говоря,у меня работает,возможно проблема в сборке PHP ?( и да,у меня не китайская кодировка )

php > echo "\xA3', 0x1234) -- ";
&#65533;', 0x1234) --
php > echo mysql_real_escape_string("\xA3', 0x1234) -- ");
&#65533;\', 0x1234) --
 

vovanium

Новичок
а прикол в том что SET NAMES не влияет на работу mysql_real_escape_string. ман
нужно mysql_set_charset юзать для смены кодировки
 

Вурдалак

Продвинутый новичок
vovanium
А какую переменную меняет эта самая mysql_set_character_set()? Я уже все переменные LIKE "%char%" поменял. Результата нет.

У кого-нибудь пример работает как надо вообще?
 

vovanium

Новичок
В твоем случае проблема наверное в том, что в big5 нет символа A327, поэтому символы обрабатываются как A3 и 27, 27 экранируется и получается A35C27 именно эта строка попадает мускулю, и он уже парсит и видит символ A35C, который и соответствует символу альфа, после которого остается 27, что соответствует кавычке.

В общем юзай utf-8 :)
 

Вурдалак

Продвинутый новичок
vovanium
Не, не, вопрос принципиальный. Мне надо составить пример работы mysql_real_escape_string(). Именно на том, что ты описал, и строится SQL injection. Мне надо её предотвратить.
 

vovanium

Новичок
Вурдалак
Ставишь кодировку binary и заливаешь :)
mysql_real_escape_string предназначена для экранирования символов указанной кодировки, эта функция не является защитником от SQL инъекций как некоторые думают ;)
Так что хочешь чтобы не было инъекций проверяй чтобы ввод был в правильной кодировке
 

Вурдалак

Продвинутый новичок
Автор оригинала: vovanium
mysql_real_escape_string предназначена для экранирования символов указанной кодировки
— мне именно это и требуется. Я повторяю: задача принципиальная. Она возникла в одном из споров, ни в каком проекте она не фигуриет. Мне требуется показать её отличие от mysql_escape_string() на уровне
PHP:
assert(mysql_real_escape_string($name) !== mysql_escape_string($name));
-~{}~ 26.06.10 18:51:

В данный момент я не могу этого добиться: она в точности действует, как и mysql_escape_string(), которая уже deprecated.
 

vovanium

Новичок
Если тебе нужно показать разницу в работе, то там не так много символов которые приводят к глюкам. Вот набросал примерчик наглядный.
PHP:
$text = "\x8c\x5c";
$f = "| %-12s | %-12s |\n";
printf($f, 'real_escape', 'escape');
echo '|' . str_repeat('-', 14) . '|' . str_repeat('-', 14) . "|\n";
// Демонстрация что SET NAMES не влияет на mysql_real_escape_string
mysql_query('SET NAMES gbk');
printf($f, bin2hex(mysql_real_escape_string($text)), bin2hex(mysql_escape_string($text)));

// А тут уже всё работает, как нужно. В случае mysql_escape_string добавляется лишний бэкслэш
mysql_set_charset('gbk');
printf($f, bin2hex(mysql_real_escape_string($text)), bin2hex(mysql_escape_string($text)));
 

Вурдалак

Продвинутый новичок
Бедные китайцы, японцы... Им жизненно необходимо проверять строку на соответствию данной кодировки (BIG5, GBK, SJIS). А ведь real_escape() могла бы просто «убивать» некорректные байты, либо всё-таки генерировать хотя бы Warning о некорректной строке. Тут чей-то просчёт однозначно, IMHO.

За код спасибо.

-~{}~ 26.06.10 21:49:

http://php.net/manual/en/function.mysql-real-escape-string.php
If binary data is to be inserted, this function must be used.
— это и был предмет спора. Мануал врёт, ага :)
 

vovanium

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

Вурдалак

Продвинутый новичок
vovanium
Там не написано «нужно использовать экранирование», там написано «this function must be used».
Эта функция неспособна экранировать бинарные данные, более того, она неспособна экранировать символы в некорректной текстовой строке. Значит мануал нагло пиз..т, не так ли?
 

vovanium

Новичок
Вурдалак
Чего это она неспособна экранировать бинарные данные? Очень даже хорошо экранирует. Не путай бинарные данные и проблемы с многобайтными кодировками.
Ну и то что эту функцию нужно использовать, не значит, что только её одной достаточно на все случаи жизни.
 

Вурдалак

Продвинутый новичок
vovanium
Я ничего не путаю. Есть такое понятие, как общий случай. Так вот, в общем случае она неспособна это делать. По умолчанию принято говорить именно об общем случае.
 

Фанат

oncle terrible
Команда форума
а прикол в том что SET NAMES не влияет на работу mysql_real_escape_string. ман
нужно mysql_set_charset юзать для смены кодировки
охренеть
If you need to change the character set of the connection, you should use the mysql_set_character_set() function rather than executing a SET NAMES (or SET CHARACTER SET) statement. mysql_set_character_set() works like SET NAMES but also affects the character set used by mysql_real_escape_string(), which SET NAMES does not.
дурдом, блин
все представления о жизни идут на смарку.
какой, получается, смысл блин в каноническом понимании mysql_real_escape_string?

If binary data is to be inserted, this function must be used.
есть еще один вариант - hex-кодирование.
Это уж точно бронебойный вариант.
 

fixxxer

К.О.
Партнер клуба
А у меня уже сто лет примерно так:
PHP:
//Mysql_Connection::connect() ...
            if (!empty($this->params['charset'])) {                                                                                                     
                if (function_exists('mysql_set_charset')) {                                                                                             
                    $result = mysql_set_charset($this->params['charset'], $this->h);                                                                    
                } else {                                                                                                                                
                    Logger()->logEvent('Warning', 'mysql_set_charset() is unavailable; using SET NAMES');                                               
                    $result = mysql_query('SET NAMES '.$this->params['charset'], $this->h);                                                             
                }                                                                                                                                       
                if (false === $result) {                                                                                                                
                    throw new Connection_Exception('Cannot set mySQL charset '.$this->params['charset']);                                       
                }                                                                                                                                       
            }
Если не рассматривать многобайтные кодировки, отличные от utf-8, то ничего страшного и нет.
 

vovanium

Новичок
fixxxer
Если не рассматривать многобайтные кодировки, отличные от utf-8, то ничего страшного и нет.
Для таких случаев mysql_real_escape_string тоже не нужен, достаточно обычного mysql_escape_string, который и работает побыстрее, за счет того что не нужно проверять кодировку.
А те кто сделал эту функцию deprecated наиболее вероятно, просто не знает тонкостей работы...
 

fixxxer

К.О.
Партнер клуба
Ага, я mysql_escape_string по умолчанию и использую. Заодно позволяет готовить запросы не требуя соедиения с базой.
А варнинг в логе будет только в случае отсутствия ysql_set_charset, что означает старую libmysqlclient - что теоретически может являться источником и других проблем; это скорее напоминание админу "обнови либу". :)
 

dimagolov

Новичок
а почему бы не использовать mysqli, который как раз учитывает кодировку соединения?
 
Сверху