Вы, кстати, не понимаете смысла защиты от инъекций.
Защищаться надо не от какого-то конкретного синтаксиса, а от самой возможности инъекции.
Не нравится DROP TABLE — через UNION можно натворить дел не меньше.
И при этом защита получается совсем несложная. Главное — не следовать дурацким советам типа «эскейпить все входящие параметры».
Ну заканчивайте уже позориться, ей-богу.
Чем больше вы тут пишете, тем сильнее выставляете напоказ свои, гм… глубокие познания :)
> при использовании библиотек mysql и mysqli все значения параметров в любом случае писал/передавал в кавычках.
во-первых, это надо было говорить с самого начала. а не после того как я объяснил, что эскейпинг без кавычек не имеет смысла. и не после собственного заявления о том, что эта функция что-либо чистит.
во-вторых, «в любом случае» это всё равно не работает.
это не работает для параметров оператор LIMIT
это не работает для идентификаторов.
в-третьих, экранировать надо не только «внешние» строки, но и «внутренние». вообще любые.
так что ваше правило «эскейпить всё внешнее» не работает. А является, по факту, причиной большинства инъекций.
вам бы очень было полезно отбросить зазнайство, признать тот факт, что вы ничего не смыслите в составлении SQL запросов, а только повторяете несколько зазубренных правил, не понимая их смысла — и попробовать, всё-таки, разобраться в вопросе.
я понимаю, что это тяжело, особенно после рассказов о том, какой негодный материал ходит к вам на собеседование. но это будет сильно полезнее тех чудес изворотливости, которые вы сейчас демонстрируете.
внутри sp_takes_string_returns_string() написано либо prepare, либо обычный запрос с подстановкой.
соответственно, либо выполняется prepare, либо парсится запрос.
проанализирована и сохранена процедура, а не запрос внутри неё.
хранимыми процедурами можно кэшировать только статические запросы, а это опять же — доли процента.
О, кстати да, про них я забыл совсем.
Они здесь вообще не при делах.
Речь о выполнении динамических запросов, и хранимые процедуры мне здесь никак не помогут.
Запрос можно поместить в процедуру, но эффективнее он от этого не станет.
«Моё самописное», равно как и любое другое осмысленное решение (в частности, PDO) делает экранирование (в смысле добавления слешей к спецсимволам) только в строках, а не во «всех параметрах».
Именно поэтому функция quote() в PDO автоматически делает SQL строкой всё, что в неё попадает — добавляя кавычки по краям. Поскольку только в строках эскейпинг имеет смысл (и обязателен). Одно без другого — кавычки без эскейпинга или эскейпинг без кавычек — не имеет смысла.
> This function must always (with few exceptions) be used to make data safe before sending a query to MySQL.
К сожалению, эту фразу писал идиот.
Несмотря на все мои усилия, её не хотят убрать из документации, оправдывая это тем, что убирают всё расширение целиком.
Попробуйте применить к анализу проблемы не цитату из документации, а свой опыт и знания, если есть. Или хотя бы попробуйте выполнить пример запроса, который я дал выше, проэскейпив $_GET['id'].
> При подготовке запроса никакие данные в плейсхолдер не подставляются, данные передаются в виде параметров к prepared statement.
1. в этом случае никакого экранирования передаваемых данных не происходит.
2. В PDO это всего лишь один из двух вариантов. По умолчанию PDO подставляет данные прямо в запрос (экранируя при этом строки), а prepare не использует.
Поэтому я пишу, что или экранирование, или подстановка. Теперь понятно?
А вы попробуйте прочесть её, всё-таки.
Можно не сейчас, можно утром, на свежую голову.
Я не смотрю снисходительно на тех, кто «нашёл эффективность в 0.5%». Я пытаюсь найти её в остальных 99.5.
В отличие от memory таблиц SQL запросы используются практически в любом скрипте. И используются неэффективно, подготавливаясь всё время заново.
Статья называется так, как называется, потому что в стандартном варианте prepared statements вообще нет возможности сделать prepare в одном скрипте, а execute — в другом. В принципе без вариантов. Независимо от типа соединения. Это всё написано в статье.
> На самом деле:
На самом деле не надо цитировать документацию по mysql, когда речь идёт о mysqli
То, что mysql мне гарантирует «нечистое» соединение — я, как бы, в курсе. Приведённый в статье код это подтверждает.
Но речь идёт о mysqli. Поэтому цитировать надо вот это: php.net/manual/ru/mysqlnd.persist.php
То есть именно только чистое соединение мне и обещают.
PDO, кстати, собирается на том же mysqlnd. И точно так же очищает соединение.
> Далее Вы называете PDO кривым велосипедом, хотя как раз в нём всё реализовано так, как заявлено в prepared statements.
Опять 25.
Во-первых, реализовано далеко не всё то, что нужно.
Во-вторых, между вызовами скриптов prepared statements не работают. выполнить один раз prepare и 100 — execute при условии, что эти вызовы приходятся на разные НТТР запросы — невозможно. А мой «велосипед» это может.
Угомонитесь уже. Пойдите поспите. Утром на свежую голову перечитайте то что тут понаписали, и попробуйте переосмыслить.
Вы знаете, количество написанной вами ерунды в последних комментариях уже зашкаливает.
> как раз через внедрение одинарной кавычки в нестроковый параметр и проводятся большинство инъекций
Сюрприз! Для нестроковых параметров кавычка не нужна вовсе:
$_GET['id'] = "1; DROP TABLE users";
поможет вам mysql_escape_string от этой (и любой другой) инъекции?
> mysql_escape_string нужно делать каждому параметру, в «чистоте» которого Вы не уверены.
1. Эта функция ничего не чистит. Она всего лишь экранирует ограничители строк и пару служебных символов для удобства. Дело не в «чистоте», а в переменности. Если мы добавляем в запрос неизвестную строку, мы обязаны её экранировать, поскольку в ней могут встретиться нуждающиеся в экранировании символы. Безотносительно к любой «чистоте» или «загрязнённости».
2. про «любые параметры, не только строковые» я привёл пример выше.
3. «Именно это я называю «внешним» параметром» — слишком крутой поворот. Слово «внешний» имеет вполне определённый смысл. И не надо его подменять в стиле прапорщика из анекдота — «Лэтають, только нызенько-нызенько!»
4. И всё равно, даже ваши «сомнения в чистоте» — слишком нечёткий критерий. На нём нельзя строить защиту. Вы и плейсхолдеры предлагаете применять избирательно — только для тех данных, по поводу которых у вас «есть сомнения»? А если сомнений у [неопытного программиста] нет, а инъекция — есть?
> делать mysql_escape_string каждому внешнему параметру, как это нужно делать в случае использования mysql
Ох. Вам сюда. Специально для тех, кто пишет эту чудовищную фразу, я писал тот пост. Ни «внешние», ни «все», ни «параметры» не имеют к функции mysql_escape_string ни малейшего отношения. У неё очень узкая область применения, не имеющая никакого отношения к защите от инъекций.
Плюс к тому лично мне не надо делать mysql_escape_string руками даже в случае использования mysql. Потому что я никогда не использую сырые функции API в коде, а пишу над ними обёртку, которая реализует все нужные механизмы для работы с БД — в том числе и использование плейсхолдеров.
> если есть инлайн-параметры, то запрос не будет подготавливаться?
Речь не об инлайн-параметрах а об обработке плейсхолдеров.
:id либо подготавливается, либо экранируется. Одно из двух. Но не одновременно.
Вы реально не читали статью, которую взялись комментировать :)
Вообще-то, там написано всё наоборот: что с «устаревшей библиотекой mysql» всё прекрасно работает.
А фиаско я потерпел с новой и рекомендованной mysqli :)
Статья не о том, какую библиотеку использовать. И даже не о том, стоит ли использовать prepared statements. Она совсем, совсем о другом.
Я писал всего лишь о том, что вы не понимаете, как работают native prepared statements.
В случае их использования никакое «экранирование определённого набора символов» в принципе не нужно и поэтому библиотека его не делает.
И наоборот — если используется экранирование, то не используется механизм prepare/execute на сервере, и — следовательно — нет даже теоретического ускорения.
Кстати, при наличии «автоматического экранирования» никакого прироста в скорости быть не может.
Или то — или другое.
Если термин «экранирование» используется по назначению.
Послушайте, вы точно поняли, о чём статья?
Во-первых, любые «следующие запросы отрабатывают сильно быстрее» из-за банального query cache.
Во-вторых, «одинаковых запросов» в типичном PHP скрипте не встречается вовсе. Повторяющиеся запросы бывают в мизерной доле случаев. То есть, никакого прироста скорости в реальной жизни native prepared statements не дают. Именно об этом написано в этой статье.
В-третьих, если говорить о PDO, то это недо-DAL. В нем нету методов, чтобы получить данные сразу из запроса. Нет плейсхолдера для оператора IN. Нельзя подготовить часть запроса.
Вы путаете понятия.
Безопасно — это плейсхолдер, не PDO. Реализуется хоть на mysql_*.
Быстро — это только так кажется. О том, как на самом деле сделать быстро — и написана эта статья. PDO из коробки этого не умеет.
Удобство — по сравнению с mysql_* — да, есть пара улучшений. Но по сравнению с нормальным DAL это такой же корявый велосипед.
Нет-нет, на веб-сервере и нет никаких расходов.
Суть HandlerSocket-а — это именно прямой интерфейс к innodb, минуя SQL. Вы его точно ни с чем не путаете?
Спасибо, наоборот — очень в тему!
Такие вот наблюдения из практического опыта очень ценны — можно будет понять что к чему, если mysql поведёт себя так же.
какая разница, кто сказал prepare?
Со слов Кости Осипова, который и принимал участие в разработке PS в Mysql, глобального кэша подготовленных запросов, о котором вы говорите (опознать по тексту) в mysql нет.
Насколько я понимаю, хранимая процедура здесь не поможет.
Речь идёт о выполнении динамических запросов, например, тех же новостей по id.
Запрос внутри хранимой процедуры точно так же будет парситься или препарироваться каждый раз при вызове процедуры.
query_cache — это тоже совсем не то. Он работает на константные запросы, и не имеет отношения к PS.
Сами же по себе PS (в mysql) работают только в пределах одного и того же соединения, глобального кэша prepared запросов не существует. Отсюда в наших 100 потоках будут делаться все те же 100 prepare+ 100 execute vs. 100 parse. И последнее явно будет выигрывать, поскольку парсинг практически одинаковый, а запрос к базе только один.
я не собираюсь ничего заменять — я всего лишь хочу расширить.
Защищаться надо не от какого-то конкретного синтаксиса, а от самой возможности инъекции.
Не нравится DROP TABLE — через UNION можно натворить дел не меньше.
И при этом защита получается совсем несложная. Главное — не следовать дурацким советам типа «эскейпить все входящие параметры».
Ну заканчивайте уже позориться, ей-богу.
Чем больше вы тут пишете, тем сильнее выставляете напоказ свои, гм… глубокие познания :)
гораздо честнее и короче было бы написать
:)
во-первых, это надо было говорить с самого начала. а не после того как я объяснил, что эскейпинг без кавычек не имеет смысла. и не после собственного заявления о том, что эта функция что-либо чистит.
во-вторых, «в любом случае» это всё равно не работает.
это не работает для параметров оператор LIMIT
это не работает для идентификаторов.
в-третьих, экранировать надо не только «внешние» строки, но и «внутренние». вообще любые.
так что ваше правило «эскейпить всё внешнее» не работает. А является, по факту, причиной большинства инъекций.
вам бы очень было полезно отбросить зазнайство, признать тот факт, что вы ничего не смыслите в составлении SQL запросов, а только повторяете несколько зазубренных правил, не понимая их смысла — и попробовать, всё-таки, разобраться в вопросе.
я понимаю, что это тяжело, особенно после рассказов о том, какой негодный материал ходит к вам на собеседование. но это будет сильно полезнее тех чудес изворотливости, которые вы сейчас демонстрируете.
соответственно, либо выполняется prepare, либо парсится запрос.
проанализирована и сохранена процедура, а не запрос внутри неё.
хранимыми процедурами можно кэшировать только статические запросы, а это опять же — доли процента.
Они здесь вообще не при делах.
Речь о выполнении динамических запросов, и хранимые процедуры мне здесь никак не помогут.
Запрос можно поместить в процедуру, но эффективнее он от этого не станет.
Именно поэтому функция quote() в PDO автоматически делает SQL строкой всё, что в неё попадает — добавляя кавычки по краям. Поскольку только в строках эскейпинг имеет смысл (и обязателен). Одно без другого — кавычки без эскейпинга или эскейпинг без кавычек — не имеет смысла.
> This function must always (with few exceptions) be used to make data safe before sending a query to MySQL.
К сожалению, эту фразу писал идиот.
Несмотря на все мои усилия, её не хотят убрать из документации, оправдывая это тем, что убирают всё расширение целиком.
Попробуйте применить к анализу проблемы не цитату из документации, а свой опыт и знания, если есть. Или хотя бы попробуйте выполнить пример запроса, который я дал выше, проэскейпив $_GET['id'].
> При подготовке запроса никакие данные в плейсхолдер не подставляются, данные передаются в виде параметров к prepared statement.
1. в этом случае никакого экранирования передаваемых данных не происходит.
2. В PDO это всего лишь один из двух вариантов. По умолчанию PDO подставляет данные прямо в запрос (экранируя при этом строки), а prepare не использует.
Поэтому я пишу, что или экранирование, или подстановка. Теперь понятно?
Можно не сейчас, можно утром, на свежую голову.
Я не смотрю снисходительно на тех, кто «нашёл эффективность в 0.5%». Я пытаюсь найти её в остальных 99.5.
В отличие от memory таблиц SQL запросы используются практически в любом скрипте. И используются неэффективно, подготавливаясь всё время заново.
Статья называется так, как называется, потому что в стандартном варианте prepared statements вообще нет возможности сделать prepare в одном скрипте, а execute — в другом. В принципе без вариантов. Независимо от типа соединения. Это всё написано в статье.
> На самом деле:
На самом деле не надо цитировать документацию по mysql, когда речь идёт о mysqli
То, что mysql мне гарантирует «нечистое» соединение — я, как бы, в курсе. Приведённый в статье код это подтверждает.
Но речь идёт о mysqli. Поэтому цитировать надо вот это: php.net/manual/ru/mysqlnd.persist.php
То есть именно только чистое соединение мне и обещают.
PDO, кстати, собирается на том же mysqlnd. И точно так же очищает соединение.
> Далее Вы называете PDO кривым велосипедом, хотя как раз в нём всё реализовано так, как заявлено в prepared statements.
Опять 25.
Во-первых, реализовано далеко не всё то, что нужно.
Во-вторых, между вызовами скриптов prepared statements не работают. выполнить один раз prepare и 100 — execute при условии, что эти вызовы приходятся на разные НТТР запросы — невозможно. А мой «велосипед» это может.
Угомонитесь уже. Пойдите поспите. Утром на свежую голову перечитайте то что тут понаписали, и попробуйте переосмыслить.
> как раз через внедрение одинарной кавычки в нестроковый параметр и проводятся большинство инъекций
Сюрприз! Для нестроковых параметров кавычка не нужна вовсе:
поможет вам mysql_escape_string от этой (и любой другой) инъекции?
> mysql_escape_string нужно делать каждому параметру, в «чистоте» которого Вы не уверены.
1. Эта функция ничего не чистит. Она всего лишь экранирует ограничители строк и пару служебных символов для удобства. Дело не в «чистоте», а в переменности. Если мы добавляем в запрос неизвестную строку, мы обязаны её экранировать, поскольку в ней могут встретиться нуждающиеся в экранировании символы. Безотносительно к любой «чистоте» или «загрязнённости».
2. про «любые параметры, не только строковые» я привёл пример выше.
3. «Именно это я называю «внешним» параметром» — слишком крутой поворот. Слово «внешний» имеет вполне определённый смысл. И не надо его подменять в стиле прапорщика из анекдота — «Лэтають, только нызенько-нызенько!»
4. И всё равно, даже ваши «сомнения в чистоте» — слишком нечёткий критерий. На нём нельзя строить защиту. Вы и плейсхолдеры предлагаете применять избирательно — только для тех данных, по поводу которых у вас «есть сомнения»? А если сомнений у [неопытного программиста] нет, а инъекция — есть?
Ох. Вам сюда. Специально для тех, кто пишет эту чудовищную фразу, я писал тот пост. Ни «внешние», ни «все», ни «параметры» не имеют к функции mysql_escape_string ни малейшего отношения. У неё очень узкая область применения, не имеющая никакого отношения к защите от инъекций.
Плюс к тому лично мне не надо делать mysql_escape_string руками даже в случае использования mysql. Потому что я никогда не использую сырые функции API в коде, а пишу над ними обёртку, которая реализует все нужные механизмы для работы с БД — в том числе и использование плейсхолдеров.
> если есть инлайн-параметры, то запрос не будет подготавливаться?
Речь не об инлайн-параметрах а об обработке плейсхолдеров.
:id либо подготавливается, либо экранируется. Одно из двух. Но не одновременно.
Вообще-то, там написано всё наоборот: что с «устаревшей библиотекой mysql» всё прекрасно работает.
А фиаско я потерпел с новой и рекомендованной mysqli :)
Статья не о том, какую библиотеку использовать. И даже не о том, стоит ли использовать prepared statements. Она совсем, совсем о другом.
В случае их использования никакое «экранирование определённого набора символов» в принципе не нужно и поэтому библиотека его не делает.
И наоборот — если используется экранирование, то не используется механизм prepare/execute на сервере, и — следовательно — нет даже теоретического ускорения.
Или то — или другое.
Если термин «экранирование» используется по назначению.
Во-первых, любые «следующие запросы отрабатывают сильно быстрее» из-за банального query cache.
Во-вторых, «одинаковых запросов» в типичном PHP скрипте не встречается вовсе. Повторяющиеся запросы бывают в мизерной доле случаев. То есть, никакого прироста скорости в реальной жизни native prepared statements не дают. Именно об этом написано в этой статье.
В-третьих, если говорить о PDO, то это недо-DAL. В нем нету методов, чтобы получить данные сразу из запроса. Нет плейсхолдера для оператора IN. Нельзя подготовить часть запроса.
Вы путаете понятия.
Безопасно — это плейсхолдер, не PDO. Реализуется хоть на mysql_*.
Быстро — это только так кажется. О том, как на самом деле сделать быстро — и написана эта статья. PDO из коробки этого не умеет.
Удобство — по сравнению с mysql_* — да, есть пара улучшений. Но по сравнению с нормальным DAL это такой же корявый велосипед.
Суть HandlerSocket-а — это именно прямой интерфейс к innodb, минуя SQL. Вы его точно ни с чем не путаете?
Такие вот наблюдения из практического опыта очень ценны — можно будет понять что к чему, если mysql поведёт себя так же.
А интерес к такому способу возник как у меня — чисто теоретически, или из практических соображений?
Со слов Кости Осипова, который и принимал участие в разработке PS в Mysql, глобального кэша подготовленных запросов, о котором вы говорите (опознать по тексту) в mysql нет.
Речь идёт о выполнении динамических запросов, например, тех же новостей по id.
Запрос внутри хранимой процедуры точно так же будет парситься или препарироваться каждый раз при вызове процедуры.
query_cache — это тоже совсем не то. Он работает на константные запросы, и не имеет отношения к PS.
Сами же по себе PS (в mysql) работают только в пределах одного и того же соединения, глобального кэша prepared запросов не существует. Отсюда в наших 100 потоках будут делаться все те же 100 prepare+ 100 execute vs. 100 parse. И последнее явно будет выигрывать, поскольку парсинг практически одинаковый, а запрос к базе только один.