понедельник, 29 июля 2019 г.

Защищённый телефон Inoi 106Z вышел в России по доступной цене

4pda.ru

Защищённый телефон Inoi 106Z вышел в России по доступной цене


В преддверии Дня Военно-десантных войск компания Inoi представила новую модель мобильного телефона 106Z. Аппарат выполнен в ударопрочном корпусе и оснащён ёмким аккумулятором, мощным динамиком и фонариком. Стоит устройство чуть больше тысячи рублей.



Inoi 106Z — кнопочный телефон в усиленном корпусе с металлическими вставками. По словам производителя, ёмкого аккумулятора на 1650 мАч хватает до двух недель работы устройства. Новинка также оснащена громким динамиком мощностью 1,5 Вт и светодиодным фонариком.
Телефон оборудован 1,8-дюймовым цветным экраном, под которым располагается классическая кнопочная клавиатура. Наличие слота для карт памяти microSD позволяет расширить встроенное хранилище на 16 ГБ. Кроме того, Inoi 106Z поддерживает работу с двумя SIM-картами.


 
Технические характеристики Inoi 106Z:
Экран
1,8"
Ёмкость аккумулятора
1650 мАч
Количество SIM-карт
две
Память
ОЗУ: 32 МБ
ПЗУ: 32 МБ с возможностью расширения до 16 ГБ
Стандарты связи
2G
Процессор
SC6531
Мультимедиа
FM-приёмник
Другие возможности
фонарик
Цвет
чёрный, хаки
Материал корпуса
ударопрочный корпус: металл, пластик
Габариты 114,3 x 51,3 x 18,5 мм
Новинка выпускается в двух вариантах расцветки: чёрный и хаки. Цена Inoi 106Z в официальном интернет-магазине производителя составляет 1190 рублей.

Как очистить кэш outlook за пару кликов

pyatilistnik.org

Как очистить кэш outlook за пару кликов

Иван Семин



Как очистить кэш outlook за пару кликов

Как очистить кэш outlook за пару кликов


Всем привет продолжаем изучение Microsoft Outlook и на очереди вопрос как очистить кэш outlook от старых адресатов. Под кэшем outlook понимается функция авто заполнения почтового адреса. Для тех, кто активно использует данный почтовый клиент, это знание будет полезно.

Очистка кэша outlook

Давайте разберемся как производится очистка кэша outlook и почему мы это делаем. Напомню когда вы создаете новое письмо, функция авто заполнения почтового адреса, помогает вам быстрее подставить нужного адресата, достаточно ввести первые буквы почтового адреса, все круто, но приходит время когда количество адресов накапливается, примером могут быть отделы бухгалтеров или еще логистов, которые часто общаются с партнерами и функция кэша outlook становится уже не то что не удобной, а геморройной, так как может подставлять адреса уже не актуальные, например уволившихся сотрудников или клиентов.
Как следствие кэш outlook нужно чистить, самый простой вариант — удалять лишние адреса прямо на месте. Если подвести курсор мыши к адресу, рядом с ним появится крестик, нажатие на который приведет к удалению из списка автодополнения. Это хорошо, если вы хотите удалить пару адресатов так, но нам допустим нужно массово все удалить.



Сделать это совсем не сложно — соответствующая кнопка есть в настройках программы
Параметры -> вкладка Почта -> Очистить список автозавершения
Очистить список автозавершения равносильно очистке кэша outlook.



Не помню можно ли это делать групповыми политиками, но точно знаю, что можно это сделать запустив скрипт powershell или просто локально в нем команду
Outlook.exe /CleanAutoCompleteCache
Думаю, вам эта заметка пригодиться, так как с такой задачей по очистке кэша outlook вы можете столкнуться на практике очень часто.

воскресенье, 21 июля 2019 г.

Как отключить уведомления в Google Chrome.

lumpics.ru

Как отключить уведомления в Google Chrome


Как отключить уведомления в Google Chrome
Активные интернет-пользователи знают, что при посещении различных веб-ресурсов можно столкнуться как минимум с двумя проблемами – надоедливой рекламой и всплывающими уведомлениями. Правда, рекламные баннеры демонстрируются вопреки нашим желаниям, а вот на постоянное получение назойливых push-сообщений каждый подписывается самостоятельно. Но когда таких уведомлений становится слишком много, возникает необходимость их отключить, и в браузере Гугл Хром это можно сделать довольно легко.
Читайте также: Лучшие блокировщики рекламы

Отключаем уведомления в Google Chrome

С одной стороны, пуш-оповещения – функция весьма удобная, так как позволяет быть в курсе различных новостей и прочей интересующей информации. С другой же, когда они приходят с каждого второго веб-ресурса, а вы как раз заняты чем-то, что требует внимания и концентрации, эти всплывающие сообщения могут быстро надоесть, а их содержимое все равно будет проигнорировано. Расскажем о том, как их отключить в десктопной и мобильной версии Хрома.

Google Chrome для ПК

Для отключения уведомлений в настольной версии веб-обозревателя потребуется выполнить несколько простых действий в разделе настроек.
  1. Откройте «Настройки» Гугл Хром, кликнув по трем вертикальным точкам в правом верхнем углу и выбрав одноименный пункт.
  2. Настройки браузера Google Chrome
  3. В отдельной вкладке откроются «Настройки», пролистайте страницу в самый низ и кликните по пункту «Дополнительные».
  4. Дополнительные настройки в браузере Google Chrome
  5. В развернувшемся списке найдите пункт «Настройки контента» и кликните по нему.
  6. Настройки контента в браузере Google Chrome
  7. На следующей странице выберите пункт «Уведомления».
  8. Уведомления в браузере Google Chrome
  9. Это и есть необходимый нам раздел. Если оставить первый пункт в списке (1) активным, веб-сайты будут отправлять вам запрос прежде, чем прислать сообщение. Для блокировки всех уведомлений его нужно отключить.
  10. Настройка уведомлений в браузере Google Chrome
Для выборочного отключения в части «Блокировать» нажмите по кнопке «Добавить» и поочередно введите адреса тех веб-ресурсов, от которых вы точно не хотите получать пуши. А вот в части «Разрешить», наоборот, можно указать так называемые доверенные веб-сайты, то есть те, от которых вы хотели бы получать push-сообщения.
Теперь вы можете выйти из настроек Google Chrome и наслаждаться интернет-серфингом без навязчивых уведомлений и/или получать пуши только от выбранных вами веб-порталов. Если требуется отключить сообщения, которые появляются при первом посещении сайтов (предложения подписаться на новостную рассылку или нечто подобное), выполните следующее:
  1. Повторите шаги 1-3 из описанной выше инструкции, чтобы перейти к разделу «Настройки контента».
  2. Выберите пункт «Всплывающие окна».
  3. Всплывающие окна в браузере Google Chrome
  4. Внесите необходимые изменения. Отключение тумблера (1) приведет к полной блокировке таких пушей. В разделах «Блокировать» (2) и «Разрешить» можно выполнить выборочную настройку – заблокировать нежелательные веб-ресурсы и добавить те, от которых вы не против получать уведомления, соответственно.
  5. Настройка всплывающих окон в браузере Google Chrome
Как только вы выполните необходимые действия, вкладку «Настройки» можно закрывать. Теперь, если вы и будете получать push-уведомления в своем браузере, то только от тех сайтов, которые вам действительно интересны.

Google Chrome для Android

Запретить показ нежелательных или навязчивых пуш-сообщений можно и в мобильной версии рассматриваемого нами браузера. Для этого потребуется выполнить следующее:
  1. Запустив Гугл Хром на своем смартфоне, перейдите в раздел «Настройки» точно таким же образом, как это делается на ПК.
  2. Настройки в мобильном Google Chrome
  3. В разделе «Дополнительные» найдите пункт «Настройки сайтов».
  4. Настройки сайтов в мобильном Google Chrome
  5. Затем перейдите в пункт «Уведомления».
  6. Уведомления в мобильном Google Chrome
  7. Активное положение тумблера говорит о том, что прежде чем начать присылать вам push-сообщения, сайты будут запрашивать разрешение. Деактивировав его, вы отключите и запрос, и уведомления. В разделе «Разрешены» будут показаны сайты, которые могут отправлять вам пуши. К сожалению, в отличие от десктопной версии веб-обозревателя, возможность настройки здесь не предусмотрена.
  8. Разрешенные уведомления в мобильном Google Chrome
  9. Выполнив необходимые манипуляции, вернитесь на шаг назад, нажав направленную влево стрелку, расположенную в левом углу окна, или соответствующую кнопку на смартфоне. Перейдите к разделу «Всплывающие окна», который находится немного ниже, и убедитесь, что переключатель напротив одноименного пункта деактивирован.
  10. Отключение всплывающих окон в мобильном Google Chrome
  11. Снова вернитесь на шаг назад, пролистайте перечень доступных параметров немного вверх. В разделе «Основные» выберите пункт «Уведомления».
  12. Меню уведомления в мобильном Google Chrome
  13. Здесь можно выполнить тонкую настройку всех отправляемых браузером сообщений (небольшие всплывающие окошки при выполнении тех или иных действий). Вы можете включить/отключить звуковое оповещение для каждого из таких уведомлений либо полностью запретить их показ. При желании это можно сделать, но мы все же не рекомендуем. Те же уведомления о скачивании файлов или переходе в режим инкогнито появляются на экране буквально на долю секунды и пропадают, не создавая никакого дискомфорта.
  14. Настройки уведомлений в мобильном Google Chrome
  15. Пролистав раздел «Уведомления» ниже, вы сможете увидеть список сайтов, которым разрешен их показ. Если в перечне присутствуют те веб-ресурсы, push-оповещения от которых вы не желаете получать, просто деактивируйте тумблер напротив его названия.
  16. Отключение уведомлений в мобильном Google Chrome
На этом все, раздел настроек мобильного Google Chrome можно закрывать. Как и в случае с его компьютерной версией, теперь вы не будете получать уведомления вообще либо же будете видеть только те, что отправлены с интересующих вас веб-ресурсов.

Заключение

Как видите, нет ничего сложного в том, чтобы отключить push-уведомления в Гугл Хром. Радует то, что сделать это можно не только на компьютере, но и в мобильной версии браузера. Если вы пользуетесь устройством на iOS, описанная выше инструкция для Android вам тоже подойдет.

суббота, 20 июля 2019 г.

Как позвонить бесплатно с компьютера на телефон даже если на телефоне нет интернета

zen.yandex.ru

Как позвонить бесплатно с компьютера на телефон даже если на телефоне нет интернета


Ситуации в жизни бывают самые разные. Но вот когда надо срочно позвонить для важного разговора, а на телефоне заканчиваются деньги это очень неудобно. В такой ситуации совсем не обязательно сломя голову бежать в к ближайшему терминалу пополнения.
Если рядом есть компьютер с интернетом, а в наше время трудно представить место, где бы его еще не было и на телефоне нужного нам абонента есть мобильный интернет, то ситуация решается просто - звонком через любой мессенджер.
Но что делать если на телефоне у того кому надо срочно позвонить нет мобильного интернета или вы вообще собираетесь звонить человеку в первый раз и не знаете есть ли у него, интернет или мессенджеры. Все что о нем известно - то что он "Иван Петрович, хороший специалист и номер телефона".
Вот здесь нам на помощь придет старый добрый скайп. Да, да с него можно звонить не только на другой скайп, но и на мобильные телефоны по всему миру. Конечно за это требуют деньги, но щедрость компании Майкрософт простирается на целый месяц пробного периода, чем мы и воспользуемся. И хотя добраться до него не просто, я думаю что мы на это способны.
Итак, что нужно делать?
1. Запускаем скайп или регистрируемся и запускаем. Выбираем пункт Звонки.
2. Внизу видим предложение звонить друзьям и отправлять SMS по дешевым тарифам, а также так приятную нашему глазу надпись Бесплатная пробная версия. Вот на нее и жмем!
3. Видим варианты с оплатой за минуты и подписку на безлимитные разговоры с Индией (или другой страной) с бесплатным пробным периодом. Выбираем эту опцию.
4. Переходим к оформлению заказа, но не оформляем, а жмем Изменить заказ.
5. Попадаем на страницу выбора подписки, в поиск вбиваем Россия или любую другую страну куда хотите звонить бесплатно со скайпа. Получаем варианты оплаты. Но и это еще не все.
6. Наш бесплатный пробный период хорошо спрятан, но мы его найдем - Жмем кнопку дополнительно и выбираем пробный период на звонки в Россию на мобильные и стационарные телефоны.
7. Оформляем заказ, указываем платежную информацию и не забываем отменить подписку в течении 27 дней, иначе снимут с карты абонплату за следующий месяц.
Но 27 дней вы можете звонить на любые телефоны с компьютера совершенно бесплатно. Пользуйтесь.

среда, 17 июля 2019 г.

5 действительно бесплатных нелинейных видеоредакторов для Windows

habr.com

5 действительно бесплатных нелинейных видеоредакторов для Windows


Gerente_Capaz 22 декабря 2016 в 14:41
Этот пост призван помочь тем, кому, как и мне, внезапно стало тесно с Movie Maker.
Видеоредакторы почти не интересовали меня до прошлого месяца, пока с друзьями мы не задумали снять что-нибудь оригинальное к Новому году. Креативность наша ограничилась тем, чтобы, говоря терминами прежде мне неизвестными, применить эффект “Разделение экрана” (Split Screen). То есть реализовать нечто такое:

Мы быстро сообразили, что дефолтный MM нам для этой задачи не подходит совершенно, и окунулись в мир нелинейных инструментов. Их оказалось невыносимо много, судя по выдаче Гугла, и само собой, выбрать один единственный, опираясь лишь на описания, не представлялось возможным. Короче говоря, было решено не тратить время на прочтение отзывов, а скачивать и тестировать.
На деле оказалось, что большинство бесплатных программ для видеоредактирования — это лишь функционально ограниченные версии софтины, которую можно купить. Ну а поскольку ради одного единственного ролика в год покупать программу не очень хочется, мы искали такую, чьи ограничения не сильно противоречат нашим потребностям. Итак, поехали.

Lightworks

Старожил среди программ видеоредактирования, про которого писали на Хабре ещё в 2010 году. Обещанные тогда исходники так никто, кажется, и не увидел, зато программа на сегодняшний день предлагает инсталляции для трех основных ОС.
Системные требования: Intel i7, AMD или другой процессор с более высокой частотой, минимум 3 Гб оперативной памяти, 200Мб свободного места на жёстком диске, Видеокарта PCI Express (NVIDIA or ATI) с минимальным объёмом памяти 1 Гб, поддержка DirectX 9.
Разработчики Lightworks заявляют, что их продукт неоднократно использовался в Голливуде, при съёмках таких фильмов как “Криминальное Чтиво” и “Волк с Уолл Стрит”.

Считается, что “фишка” в Lightworks — это безупречно реализованный инструмент нарезки видео. О безупречности и интуитивности я судить не берусь — после Movie Maker меня все нелинейные редакторы поначалу вгоняли в краткосрочный ступор.

По делу


  • Заявленные фичи помимо базовых: поддержка редактирования с нескольких камер, 100 встроенных пресетов с видеоэффектами, вывод изображения на второй монитор.
  • Открывает форматы: ProRes, Avid DNxHD, AVC-Intra, DVCPRO HD, RED R3D, DPX, AVCHD и HD 422
  • Форматы экспорта: “архив” Lightworks или же напрямую на YouTube/Vimeo в формате MPEG-4.
  • Главное ограничение бесплатной версии: позволяет экспортировать видео только непосредственно на YouTube или Vimeo, не даёт сохранить проект на компьютере в читабельном формате.

VSDC

VSDC выдаётся гуглом среди первых результатов по запросу “бесплатный видеоредактор”, несмотря на то, что на Хабре про него никто не писал. Интерфейс здесь современностью не отличается и кому-то даже может отдалённо напомнить Офис. Впрочем, бесплатность и широкий набор инструментов, этот незначительный недостаток уравновешивают. Главными преимуществами VSDC разработчики называют полную совместимость со всеми известными форматами, как на импорте, так и на экспорте. Причём в настройках можно выбрать даже кодек H265/HEVC, который как известно, даёт максимальное качество изображения при высокой степени сжатия файла. Те кто планируют обрабатывать видео с разрешением 4K на этом моменте особенно порадуются, поскольку в других бесплатных инструментах такой возможности замечено не было.

Системные требования: Intel, AMD или другие совместимые процессоры с частотой от 1.5 Гц, 256 Мб оперативной памяти, 50 Мб свободного места на жёстком диске.
Слегка смутило всплывающее окно при запуске программы, на котором предлагался апгрейд для версии ПРО, но из написанного стало понятно, что основная фишка апгрейда — это аппаратное ускорение на экспорте, а все остальные фичи ничем от бесплатной версии не отличаются.
  • Заявленные фичи помимо базовых: более 20 пресетов цветокоррекции в стиле Инстаграм-фильтров, множество аудио и видеоэффектов, включая наложение слоёв, наложение маски на объект, Chroma Key, визард для создания слайдшоу.
  • Открывает форматы: AVI, QuickTime, HDVideo, WindowsMedia, DVD, VCD/SVCD, MPEG/MPG, DV, AMV, MTV, NUT, H.264/MPEG-4, DivX, XviD, MJPEG
  • Форматы экспорта: AVI, DVD, VCD/SVCD, MPEG, MP4, M4V, MOV, 3GP/3G2, WMV, MKV, RM/RMVB, FLV, SWF, AMV, MTV
  • Главное ограничение бесплатной версии: помимо аппаратного ускорения, ограничения либо отсутствуют, либо обозначены неявно. Если кто-то найдёт — жду в комментариях.

Davinci Resolve


От Давинчи у меня осталось двоякое впечатление. С одной стороны, по набору инструментов и интерфейсу, он заметно выбивается на фоне остальных бесплатных программ — ощущение действительно мощного профессионального видеоредактора. Причем, как и в случае с VSDC, нет никаких существенных для среднестатистического пользователя различий между бесплатной версией и коммерческой за $995 (!). С другой стороны, Davinci Resolve предъявляет высокие требования к видеокарте, так как применение изменений здесь реализовано в режиме реального времени. Это, безусловно, впечатляет меня как пользователя, но также периодически роняет весь процесс на недостаточно мощных компьютерах.
Системные требования: 12 Гб оперативной памяти (рекомендовано 16 Гб), мощная видеокарта с технологией CUDA и рекомендованным объёмом памяти от 3 Гб, монитор с разрешением 1920х1080 и выше.
Самый большой акцент в видеоредакторе Davinci Resolve делается на инструменты цветокоррекции. Если для вас идеального голливудского уровня изображение не является приоритетом, то возможно, вам, как и мне, не хватит терпения разобраться в настройках. Но судя по тьюториалам, с цветом Давинчи действительно способен творить чудеса.
  • Заявленные фичи помимо базовых: профессиональная цветокоррекция, кадрирование, микширование звука, работа с текстом, поддержка OpenFX плагина для добавления эффектов переходов.
  • Открывает форматы: Davinci открывает большинство популярных видеоформатов, однако предпочтительный рабочий для него — ProRes, поэтому перед началом рекомендуется конвертация.
  • Форматы экспорта: AVI, QuickTime, MP4, MOV, APNG
  • Главное ограничение бесплатной версии: Davici Resolve явно рассчитывает на аудиторию профессионалов видеоредактирования, поэтому в платную версию вынесли такие скорее всего неважные для обычного пользователя плюшки как совместный монтаж, шумоподавление и редактирование стереоскопического 3D.

Hitfilm Express

В Hitfilm Express делают упор на спецэффекты, и это становится понятно с первых секунд на сайте, который выполнен с использованием эффектов из Железного Человека (к моменту написания, правда, поменяли на Звездные Войны). Тут вам и «тряска», и «дождь», и «кровавые брызги». Второе конкурентное преимущество этого редактора — композитинг — инструменты для совмещения визуальных эффектов и 2D/3D анимации, которые, если верить форумам, ничуть не уступают Adobe After Effect.

Системные требования: 1.2 Гб свободного места на жёстком диске, 4 Гб оперативной памяти (рекомендуемый объём — 8 Гб), графический процессор с 2 Гб памяти (минимум 512 Мб).
У Hitfilm Express довольно простой и понятный интерфейс по сравнению со многими нелинейными редакторами, но в то же время обидно, что поддержку таких форматов, как например, AVCHD and MPEG-2 вынесли в раздел платных возможностей, которые можно получить за $10.
  • Заявленные фичи помимо базовых: более 180 визуальных эффектов и пресетов, 3D композитинг, анимация и графический редактор, Chroma Key.
  • Открывает форматы: Quicktime, AVI, MOV, MP4, MPEG-1, MXF, DV, HDV and WMV
  • Форматы экспорта: QuickTime, MP4, AVI и прямая загрузка на YouTube direct
  • Главное ограничение бесплатной версии: на экспорте максимальное разрешение 1080p и глубина цвета 8 бит, в то время как в платной версии максимальное разрешение — 8K UHD 32-битовая глубина. Кроме того, в бесплатной версии нет поддержки плагинов OpenFX.

OpenShot

Говоря о бесплатных видеоредакторах для Windows, невозможно обойти стороной бета-версию OpenShot — один из первых инструментов с открытым кодом и восьмилетней историей. Кстати, хабровчанам этот проект, возможно, будет интересен своей кампанией на Kickstarter, где в 2013 году на новую версию создатель OpenShot собрал более чем в 2 раза больше требуемой суммы. Изначально разработанный для Linux, этот инструмент претендовал на звание самого понятного профессионального видеоредактора.

Системные требования: 8 Гб оперативной памяти (минимум 2 Гб), 64-битный 4-ядерный процессор, видеокарта с поддержкой OpenGL 3.2 и 2 Гб памяти.

Интерфейс Openshot, пожалуй, действительно самый минималистичный из всех, что мне довелось увидеть, и то же самое можно сказать о наборе возможностей. При первом запуске программы каждый шаг сопровождается всплывающей подсказкой, хотя пункты меню мне показались настолько интуитивными, что запутаться в них невозможно. Признаюсь, до полноценного тестирования дело так и не дошло — после нескольких раз принудительного завершения работы Openshot, моего терпения перестало хватать.
  • Заявленные фичи помимо базовых: видеоэффекты, в том числе Chroma Key, анимированные 3D заголовки, мультипликация и наложение водяных знаков.
  • Открывает: все форматы FFmpeg, включая WebM, AVSHD, HEVC
  • Экспорт: есть как возможность прямой загрузки на YouTube и Vimeo, так и возможность кастомизировать настройки экспорта, самостоятельно выбрав битрейт, формат и кодек.
Само собой, это далеко не все бесплатные видеоредакторы, которые существуют сегодня на рынке. Однако, протестировав каждый из них, я могу поручиться по крайней мере за то, что вас не ждут сюрпризы в виде вотермарки, отсутствия звука после экспорта или невозможности сохранить проект, как это случается с некоторыми «псевдобесплатными программами».
Добавляйте в комментариях свои варианты, собрать более полный список нелинейных инструментов для основательного тестирования было бы неплохо.

понедельник, 15 июля 2019 г.

Настройка файла php.ini | Losst

losst.ru

Настройка файла php.ini | Losst

admin

PHP - это один из самых популярных языков программирования для создания сайтов и веб-приложений. На нем разработано множество готовых систем управления контентом для блогов, сайтов фирм или даже интернет-магазинов. Несмотря на то что у этого языка есть свои недостатки, он достаточно прост в освоении и поэтому очень часто используется для разработки новых сайтов.
Интерпретатор php может поставляться в виде модуля для Apache, выполнять скрипты из командной строки или в виде отдельного сервиса php-fpm. Эти сервисы отличаются своими возможностями, и предназначением, но для любого вида интерпретатора нужно задать базовые настройки, например, рабочая папка, включенные расширения, отображение ошибок и так далее. Все эти настройки задаются через файл php.ini. В этой инструкции мы рассмотрим как выполняется настройка файла php.ini в операционных системах Linux, хотя все информация подойдет и для Windows.
Если у вас еще не установлен интерпретатор языка программирования php, то вы можете ознакомиться со статьей установка lamp в Ubuntu 16.04.

Расположение и синтаксис php.ini

Для каждой версии интерпретатора конфигурационный файл php.ini находится в отдельной папке. Но все конфигурационные файлы находятся в папке /etc/php, например, /etc/php5:
ls /etc/php5/
php
Папка conf.d содержит общие настройки для различных расширений и модулей, они не будут нас сейчас интересовать. Более интересны следующие три папки - apache, cli и fpm. В них содержатся конфигурационные файлы php.ini для каждого из этих интерпретаторов.
Если вы собираетесь использовать несколько из этих интерпретаторов, то вам придется указывать настройки для каждого из них отдельно. Вы можете убедиться, что в каждой из папок лежит файл php.ini.
Что касается синтаксиса файла, то он разделен на секции, сначала идет секция настройки php, которая разделена на подсекции в зависимости от типа настроек, дальше идут секции настройки разных модулей. Синтаксис самих настроек очень прост, он соответствует привычному синтаксису ini  файлов. Строка начинается с имени настройки, затем следует знак равно, а за ним значение:
имя_настройки=значение_параметра
Символами [] обозначается имя секции, например, [PHP], а символ ; означает комментарий, он и все символы после него не читаются интерпретатором. А теперь рассмотрим как выполняется настройка php.ini и переберем самые важные параметры.

Настройка файла php.ini

Для удобства ориентирования мы разобьем все параметры по категориях в зависимости от их назначения. Вам будет достаточно найти нужный параметр и изменить его значение. А теперь откройте файл настроек php, например, для модуля apache и перейдем к настройке. Чтобы избежать ошибок не добавляйте новые строки, а ищите уже существующие и изменяйте значения на нужные:
sudo gedit /etc/php5/apache/php.ini
php1
Сначала идет немного информации о самом файле в виде комментариев, затем интересующие нас настройки.

Вывод ошибок в php

Настройка php 7 обычно начинается с конфигурации вывода ошибок. Все настройки вывода ошибок находятся в разделе Error handling and logging. По умолчанию вывод ошибок на экран во время выполнения скрипта отключен. Это сделано для того, чтобы пользователи не смогли увидеть ничего лишнего. Вместо этого, все ошибки записываются в лог файл. Если вы используете php на домашнем компьютере, то такие меры не нужны и вы можете сразу выводить все на экран:
display_errors=off
php3
Замените off на on. В php используются различные типы ошибок, например, критические, предупреждения, ошибки синтаксиса, с помощью строки error_reporting вы можете включить вывод только определенных типов ошибок:
error_reporting = E_ALL  & ~E_DEPRECATED
Если нужно объединить несколько типов ошибок, то используйте символ &, а для отключения отображения поставьте перед типом знак ~. Приведенный выше пример отображает все ошибки (E_ALL), кроме сообщений об устаревших функциях (E_DEPRECATED). Вы можете отключить все типы использовав 0:
error_reporting = 0
Включите запись ошибок php в лог файл, если не выводите их на экран:
log_errors = On
Чтобы не засорять лог однотипными сообщениями можно игнорировать повторяющиеся ошибки в пределах одного исполнения:
ignore_repeated_errors = On

Ограничения ресурсов

php6
Если бы скрипты php никак не ограничивались в ресурсах, то они запросто могли бы перегрузить сервер и не дать ему нормально работать. Поэтому, по умолчанию php устанавливает жесткие ограничения, но, возможно, вам нужно будет их немного ослабить.
По умолчанию максимальное время выполнения скрипта - 30 секунд, сделаем минуту:
max_execution_time = 30
Если указать 0, то скрипт может выполняться бесконечно. Вы также можете ограничить время, на протяжении которого скрипт будет загружать данные, 60 секунд:
max_input_time=60
Максимальное количество переменных в GET и POST:
max_input_vars = 1000
Следующий параметр задает максимальное количество памяти, которую может использовать один скрипт во время своего выполнения, в мегабайтах:
memory_limit = 128M
Максимальный размер данных, передаваемых в POST запросе тоже ограничивается, размер по умолчанию - 8 Мегабайт:
post_max_size = 8M
Вы можете ограничить область действия php в системе с помощью опции openbase_dir, она указывает папку, выше которой скрипт не может получить доступ к файловой системе:
open_basedir = /var/www/
С помощью директив disable_functions и disable_classes вы можете отключить использование в скриптах определенных функций или классов, например, это может быть полезно для веб-хостингов. В этом примере мы отключаем использование функции ini_set, которая позволяет менять настройки php из скрипта:
disable_functions = ini_set

Директории по умолчанию

php4
Файл настройки php.ini позволяет указать пути в файловой системе по умолчанию для различных действий. Вы можете задать папки где система будет искать скрипты, если вы попытаетесь подключить их с помощью инструкции include:
include_path = ".:/usr/share/php5:/usr/share/php5/PEAR"
Папка с модулями php:
extension_dir="./"
Папка для записи временных файлов:
sys_temp_dir = "/tmp"

Загрузка файлов

php5
Для того чтобы пользователи могли загружать свои файлы на сервер, например, фото, нужно включить эту функцию в php:
file_uploads = On
Максимальный размер загружаемого файла:
upload_max_filesize = 2M
Максимальное количество файлов, которые может загрузить один скрипт:
max_file_uploads = 20
Настройка php.ini практически завершена, нам остались лишь расширения.

Настройка расширений

Расширения позволяют очень сильно увеличить функциональность php. Например, благодаря расширениям вы можете использовать в своих скриптах базы данных mysql, postgresql, mysqli, sqlite, графическую библиотеку gd и многое другое. Все это включается в этом разделе.
Для включения расширения достаточно убрать комментарий перед строкой с его командой, например:
extension=php_mysql.so
extension=php_mbstring.so
extension=php_pgsql.so

php2
Обратите внимание, что для windows расширение будет иметь формат dll, но для linux нужно использовать so. В следующих секциях идет настройка каждого из расширений, но мы их рассматривать не будем потому что они обычно не требуют настройки.

Выводы

В этой статье мы рассмотрели как выполняется настройка php на сервере или обычном компьютере для разработки веб-сайтов. Файл настроек php имеет довольно простую структуру и с ним довольно не сложно справиться. После завершения всех настроек и сохранения изменений не забудьте перезагрузить веб-сервер или сервис php-fpm.
Вообще говоря, php-fpm это отдельная тема, потому что там есть много дополнительных настроек, и, возможно, мы рассмотрим его в одной из следующих статей. Если у вас остались вопросы, спрашивайте в комментариях!

воскресенье, 7 июля 2019 г.

Команда GPResult: диагностика результирующих групповых политик

winitpro.ru

Команда GPResult: диагностика результирующих групповых политик

itpro

Утилита GPResult.exe – представляет собой консольное приложение, предназначенное для анализа настроек и диагностики групповых политик, которые применяются к компьютеру и/или пользователю в домене Active Directory. В частности, GPResult позволяет получить данные результирующего набора политик (Resultant Set of Policy, RSOP), список примененных доменных политик (GPO), их настройки и детальную информацию об ошибках их обработки. Утилита входит в состав ОС Windows начиная со времен Windows XP. Утилита GPResult позволяет ответить на такие вопросы: применяется ли конкретная политика к компьютеру, какая именно GPO изменила ту или иную настройку Windows, разобраться с причинами долгого применения GPP/GPO.
В этой статье мы рассмотрим особенности использования команды GPResult для диагностирования работы и отладки применения групповых политик в домене Active Directory.
Изначально для диагностики применения групповых политик в Windows использовалась графическая консоль RSOP.msc, которая позволяла получить настройки результирующих политик (доменных + локальных), примененные к компьютеру и пользователю в графическом виде аналогичном консоли редактора GPO (ниже на примере представления консоли RSOP.msc видно, что настройки обновлений заданы политикой WSUS_SERVERS).




Однако, консоль RSOP.msc в современных версиях Windows использовать нецелесообразно, т.к. она не отражает настройки, примененные различными расширениями групповых политик (client side extensions — CSE), например GPP (Group Policy Preferences), не позволяет выполнять поиск, предоставляет мало диагностической информации. Поэтому на данный момент именно команда GPResult является основным средством диагностики применения GPO в Windows (в Windows 10 даже появляется предупреждение, что RSOP не дает полный отчет в отличие от GPResult).

Использование утилиты GPResult.exe

Команда GPResult выполняется на компьютере, на котором нужно проверить применение групповых политик. Команда GPResult имеет следующий синтаксис:
GPRESULT [/S <система> [/U <пользователь> [/P <пароль>]]] [/SCOPE <область>]           [/USER <имя_конечного_пользователя>] [/R | /V | /Z] [(/X | /H) <имя_файла> [/F]]
Чтобы получить подробную информацию о групповых политиках, которые применяются к данном объекту AD (пользователю и компьютеру), и других параметрах, относящихся к инфраструктуре GPO (т.е. результирующие настройки политик GPO – RsoP), выполните команду:
Gpresult /r
Результаты выполнения команды разделены на 2 секции:
  • COMPUTER SETTINGS (Конфигурация компьютера) – раздел содержит информацию об объектах GPO, действующих на компьютер (как объект Active Directory);
  • USER SETTINGS – пользовательский раздел политик (политики, действующие на учетную запись пользователя в AD).
Вкратце пробежимся по основным параметрам/разделам, которые нас могут заинтересовать в выводе GPResult:
  • Site Name (Имя сайта:)– имя сайта AD , в котором находится компьютер;
  • CN – полное каноническое пользователя/ компьютера, для которого были сгенерированы данные RSoP;
  • Last time Group Policy was applied  (Последнее применение групповой политики)– время, когда последний раз применялись групповые политики;
  • Group Policy was applied from (Групповая политика была применена с)– контроллер домена, с которого была загружена последняя версия GPO;
  • Domain Name и Domain Type (Имя домена, тип домена)– имя и версия схемы домена Active Directory;
  • Applied Group Policy Objects (Примененные объекты групповой политики) – списки действующих объектов групповой политики;
  • The following GPOs were not applied because they were filtered out (Следующие политики GPO не были применены, так как они отфильтрованы)— не примененные  (отфильтрованные) GPO;
  • The user/computer is a part of the following security groups (Пользователь/компьютер является членом следующих групп безопасности) – доменные группы, в которых состоит пользователь.



В нашем примере видно, что на объект пользователя действуют 4 групповые политики.
Если вы не хотите, чтобы в консоль одновременно выводилась информация и о политиках пользователя и о политиках компьютера, вы можете с помощью опции /scope вывести только интересующий вас раздел. Только результирующие политики пользователя:
gpresult /r /scope:user
или только примененные политики компьютера:
gpresult /r /scope:computer
Т.к. утилита Gpresult выводит свои данные непосредственно в консоль командной строки, что бывает не всегда удобно для последующего анализа, ее вывод можно перенаправить в буфер обмена:
Gpresult /r |clip
или текстовый файл:
Gpresult /r > c:\gpresult.txt
Чтобы вывести сверхподробную информацию RSOP, нужно добавить ключ /z.
Gpresult /r /z

HTML отчет RSOP с помощью GPResult

Кроме того, утилита GPResult может сгенерировать HTML-отчет по примененным результирующим политикам (доступно в Windows 7 и выше). В данном отчете будет содержаться подробная информация обо всех параметрах системы, которые задаются групповыми политиками и именами конкретных GPO, которые их задали (получившийся отчет по структуре напоминает вкладку Settings в консоли управления доменными групповыми политиками – GPMC). Сгенерировать HTML отчет GPResult можно с помощью команды:
GPResult /h c:\gp-report\report.html /f




Чтобы сгенерировать отчет и автоматически открыть его в браузере, выполните команду:
GPResult /h GPResult.html & GPResult.html
В HTML отчете gpresult содержится довольно много полезной информации: видны ошибки применения GPO, время обработки (в мс.) и применения конкретных политик и CSE (в разделе Computer Details -> Component Status). Например, на скриншоте выше видно, что политика Enforce password history с настройками 24 passwords remember применена политикой Default Domain Policy (столбец Winning GPO). Как вы видите, такой отчет HTML намного удобнее для анализа применённых политик, чем консоль rsop.msc.

Получение данных GPResult с удаленного компьютера

GPResult может собрать данные и с удаленной компьютера, избавляя администратора от необходимости локального или RDP входа на удаленный компьютер.  Формат команды сбора данных RSOP с удаленного компьютера такой:
GPResult /s server-ts1 /r
Аналогичным образом вы можете удаленно собрать данные как по пользовательским политикам, так и по политиками компьютера.

Пользователь username не имеет данных RSOP

При включенном UAC запуск GPResult без повышенных привилегий выводит параметры только пользовательского раздела групповых политик. Если нужно одновременно отобразить оба раздела (USER SETTINGS и COMPUTER SETTINGS), команду нужно запускать в командной строке, запущенной с правами администратора. Если командная строка с повышенными привилегиями запущена от имени учетной записи отличной от текущего пользователя системы, утилита выдаст предупреждение INFO: The user “domain\user” does not have RSOP data (Пользователь «domain\user» не имеет данных RSOP). Это происходит потому, что GPResult пытается собрать информацию для пользователя, ее запустившего, но т.к. данный пользователь не выполнил вход (logon) в систему, информация RSOP для него отсутствует. Чтобы собрать информацию RSOP по пользователю с активной сессией, нужно указать его учетную запись:
gpresult /r /user:tn\edward




Если вы не знаете имя учтённой записи, которая залогинена на удаленном компьютере, учетную запись можно получить так:
qwinsta /SERVER:remotePC1
Также проверьте время (и часовой пояс) на клиенте. Время должно соответствовать времени на PDC (Primary Domain Controller).

Следующие политики GPO не были применены, так как они отфильтрованы

При траблшутинге групповых политик стоит также обращать внимание на секцию: The following GPOs were not applied because they were filtered out (Следующие политики GPO не были применены, так как они отфильтрованы). В этой секции отображается список GPO, которые по той или иной причине не применяются к этому объекту. Возможные варианты, по которым политика может не применяться:
  • Filtering: Not Applied (Empty)  (Фильтрация:  Не применено (пусто)) – политика пустая (применять, собственно, нечего);
  • Filtering: Denied (Unknown Reason) (Фильтрация:  Не применено (причина неизвестна)) – скорее всего у пользователя или компьютера отсутствуют разрешения на чтение/применение этой политики (разрешения настраиваются на вкладке Security в консоли управления доменными GPO — GPMC (Group Policy Management Console);
  • Filtering: Denied (Security) (Фильтрация:  Отказано (безопасность)) — в секции Apply Group Policy указан явный запрет в разрешении Apply group policy либо объект AD не входит в список групп в разделе Security Filtering настроек GPO


Также вы можете понять должна ли применяться политика к конкретному объекту AD на вкладке эффективных разрешений (Advanced -> Effective Access).
Итак, в этой статье мы рассмотрели особенности диагностики применения групповых политик с помощью утилиты GPResult и рассмотрели типовые сценарии ее использования.

вторник, 2 июля 2019 г.

Защита от SQL-инъекций в PHP и MySQL

habr.com

Защита от SQL-инъекций в PHP и MySQL


К своему удивлению, я не нашёл на Хабре исчерпывающей статьи на тему защиты от инъекций. Поэтому решил написать свою.
Несколько пространный дисклеймер, не имеющий прямого отношения к вопросу
Давайте признаем факт: количество статей (и комментариев) на тему защиты от SQL-инъекций, появившихся на Хабре в последнее время, говорит нам о том, что поляна далеко не так хорошо истоптана, как полагают некоторые. Причём повторение одних и тех же ошибок наводит на мысль, что некоторые заблуждения слишком устойчивы, и требуется не просто перечисление стандартных техник, а подробное объяснение — как они работают и в каких случаях должны применяться (а в каких — нет).
Статья получилась довольно длинной — в ней собраны результаты исследований за несколько лет — но самую важную информацию я постараюсь компактно изложить в самом начале, а более подробные рассуждения и иллюстрации, а так же различные курьёзы и любопытные факты привести в конце. Также я постараюсь окончательно развеять множественные заблуждения и суеверия, связанные с темой защиты от инъекций.
Я не буду пытаться изображать полиглота и писать рекомендации для всех БД и языков разом. Достаточное количество опыта у меня есть только в веб-разработке, на связке PHP/MySQL. Поэтому все практические примеры и рекомендации будут даваться для этих технологий. Тем не менее, изложенные ниже теоретические принципы применимы, разумеется, для любых других языков и СУБД.
Сразу отвечу на стандартное замечание про ORM, Active record и прочие query builders: во-первых, все эти прекрасные инструменты рождаются не по мановению волшебной палочки из пены морской, а пишутся программистами, используя всё тот же грешный SQL. Во-вторых, будем реалистами: перечисленные технологии — хорошо, но на практике сырой SQL постоянно встречается нам в работе — будь то legacy code или развесистый JOIN, который транслировать в ORM — себе дороже. Так что не будем прятать голову в песок и делать вид, что проблемы нет.
Хоть я и постарался подробно осветить все нюансы, но, вполне возможно, некоторые из моих выводов могут показаться неочевидными. Я вполне допускаю, что мой контекст и контексты читателей могут различаться. И вещи, которые кажутся мне сами собой разумеющимися, не являются таковыми для некоторых читателей. В этом случае буду рад вопросам и уточнениям, которые помогут мне исправить статью, сделав её более понятной и информативной.
Ещё только начав интересоваться темой защиты от инъекций, я всегда хотел сформулировать набор правил, который был бы одновременно исчерпывающим и компактным. Со временем мне это удалось:

Правила, соблюдение которых гарантирует нас от инъекций


  1. данные подставляем в запрос только через плейсхолдеры
  2. идентификаторы и ключевые слова подставляем только из белого списка, прописанного в нашем коде.
Всего два пункта.
Разумеется, практическая реализация этих правил нуждается в более подробном освещении.
Но у этого списка есть большое достоинство — он точный и исчерпывающий. В отличие от укоренившихся в массовом сознании правил «прогонять пользовательский ввод через mysql_real_escape_string» или «всегда использовать подготовленные выражения», мой набор правил не является катастрофическим заблуждением (как первое) или неполным (как второе).
Но вперёд, читатель — перейдём уже к подробному разбору.

Плейсхолдеры — подстановка данных
В принципе, тут всё просто: любые данные должны попадать в запрос не напрямую, а через некоего представителя, подстановочное выражение.
Запрос пишется в таком, например, виде,
SELECT * FROM table WHERE id > ? LIMIT ?
а данные добавляются и обрабатываются отдельно.
Но чем это лучше «обычного эскейпинга»? А всем:
  • Во-первых, «эскейпинг» вообще не имеет никакого отношения к безопасности. Представьте себе!
  • Во-вторых, код становится банально короче. Никаких тебе mysql_real_escape_string(), ни даже intval() — вся обработка скрыта внутри.
  • В-третьих, код становится проще. Не нужно запоминать различные правила для форматирования разных частей запроса
  • В-четвёртых, использование плейсхолдеров (при условии, что они корректно обрабатываются) гарантирует нас от инъекций через данные. «Это же очевидно!» — скажете вы — «стоило ли городить отдельный пункт?» Стоило. Тот же эскейпинг защиту не гарантирует. Плюс сокращение правил форматирования до одного — перепутать что-либо тут будет затруднительно
  • В-пятых — и в самых главных — мы обрабатываем данные ровно там, где нужно! Это очень важный момент, который многие не понимают. В классических «похапешных» учебниках форматирование данных для SQL разбросано по всему коду. А в старых версиях PHP оно и вовсе начиналось ещё даже до начала выполнения кода — что совсем уже ни в какие ворота не лезет! Такая ситуация приводит к тому, что одни данные форматируются дважды, другие — только наполовину, а третьи — и вовсе ни разу или совсем не так, как нужно, без малейшей пользы. При этом отформатированные для SQL данные вдруг оказываются в HTML или куках, что тоже не прибавляет радости пользователям и разработчикам.
Поэтому самым лучшим вариантом будет форматировать данные непосредственно перед исполнением запроса — таким образом мы всегда будем уверены в том, что данные форматируются корректно, это делается только один раз, и отформатированные данные попадут строго по назначению — в БД и никуда больше.
И вот как раз целям такой — своевременной, безопасной и корректной — обработки данных и служат плейсхолдеры, давая нам гарантию безопасности, в то же время упрощая код.
Примеры использования:
Из мануала:
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(array("%$_GET[name]%"));
$data = $stmt->fetchAll();
Пример того, к чему стоит стремиться:
$ban = $db->getRow("SELECT 1 FROM ban WHERE ip = inet_aton(s:)", $ip);
Как видите, совсем несложно, а так же — при умелом использовании — выходит гораздо короче, чем составлять запрос руками. Вы всё ещё хотите писать по-старинке?
Важное замечание: Разумеется, подстановка данных через плейсхолдеры должна производиться всегда, вне зависимости от источника данных или каких бы то ни было других условий.
Идентификаторы и ключевые слова — белые списки
Подавляющее большинство статей, посвящённых инъекциям, совершенно упускают этот момент из виду. Но реальность такова, что в ней мы сталкиваемся с необходимостью подставлять в запрос не только данные, но и другие элементы — идентификаторы (имена полей и таблиц) и даже элементы синтаксиса, ключевые слова. Пусть даже такие незначительные, как DESC или AND, но требования к безопасности таких подстановок всё равно должны быть не менее строгими!
Разберем довольно банальный случай.
У нас есть база товаров, которая выводится пользователю в виде HTML таблицы. Пользователь может сортировать эту таблицу по одному из полей, в любом направлении.
То есть, как минимум, со стороны пользователя к нам приходит имя колонки и направление сортировки.
Подставлять их в запрос напрямую — гарантированная инъекция. Привычные методы форматирования здесь не помогут. Подготовленные выражения ни с идентификаторами, ни с ключевыми словами не приведут ни к чему, кроме сообщения об ошибке.
Единственное решение — белый список.
Это, разумеется, не бином ньютона, и многие разработчики легко по ходу дела реализуют эту парадигму на ходу, впервые столкнувшись с необходимостью подстановки имени поля в запрос. Тем не менее, статья о защите от инъекций без этого правила будет неполной, а сама защита — дырявой.
Суть метода заключается в том, что все возможные варианты выбора должны быть жёстко прописаны в нашем коде, и в запрос должны попадать только они, на основании пользовательского ввода.
Пример применения:

$order   = isset($_GET['order']) ? $_GET['order'] : ''; 
$sort    = isset($_GET['sort'])  ? $_GET['sort']  : '';

$allowed = array("name", "price", "qty"); 
$key     = array_search($sort,$allowed); 
$orderby = $allowed[$key]; 
$order   = ($order == 'DESC') ? 'DESC' : 'ASC'; 
$query   = "SELECT * FROM `table` ORDER BY $orderby $order"; 
Раньше я предполагал, что для идентификаторов достаточно плейсхолдера. Но со временем пришло понимание недостатков этого метода:
  • во-первых, в случае неверного имени поля запрос вызовет ошибку. А ошибки — это всегда плохо
  • во-вторых, и гораздо более важных: если подставлять имена полей без предварительной фильтрации, автоматом, только эскейпя их, можно получить инъекцию другого рода — ведь пользователь тогда может вписать в те имена полей, которые ему изменять не положено! Скажем, если мы формируем SQL запрос автоматически на базе массива $_POST (при этом правильно форматируя имена полей!), то хакер при регистрации добавляет в форму поле admin со значением «1» и становится админом
Так что теперь я использую оба метода:
Сначала получаю идентификатор из белого списка.
А потом добавляю его через плейсхолдер — просто чтобы не заниматься форматированием вручную. И для единообразия. В этом случае последняя строчка будет выглядеть, как
$query = "SELECT * FROM `table` ORDER BY n: $order";
В принципе, этой информации достаточно, чтобы начать писать полностью безопасные запросы. Но, как водится, в реальной жизни случаются разные нюансы, да и разобраться с механизмом плейсхолдеров хотелось бы поподробнее.

Работа с плейсхолдерами

Для начала нужно понимать, что существует два варианта реализации плейсхолдеров — серверный и клиентский:
  • В первом случае запрос так и уходит на сервер с плейсхолдерами, а данные отправляются отдельно от него. По-английски называется native prepared statements — «родные» подготовленные выражения — то есть, обработка плейсхолдеров осуществляется самой СУБД, на сервере. Для краткости я буду использовать наименование «серверные плейсхолдеры».
  • Во втором случае данные форматируются и подставляются в строку запроса на место плейсхолдеров прямо на клиенте, формируя классический SQL запрос, который затем уходит в базу обычным порядком.
Каждый из способов имеет свои достоинства и недостатки, которые мы рассмотрим ниже.
Следует также помнить, что PDO сидит на этих двух стульях одновременно — по умолчанию работает по второму варианту, лишь эмулируя первый. Эту функциональность можно отключить, заставив PDO отправлять на сервер данные отдельно от запроса.
$dbh->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
Но поскольку даже эмуляция делается без участия программиста, ниже мы будем рассматривать PDO как представителя серверных плейсхолдеров.
Серверные плейсхолдеры
Для начала давайте сформулируем — почему вообще возможны инъекции?
По факту, SQL запрос представляет собой программу. Полноценную программу — с операторами, переменными и строковыми литералами. Проблема же состоит в том, что мы эту программу собираем динамически, на ходу. В отличие от наших PHP скриптов, которые написаны раз и навсегда, и не изменяются на основе поступающих данных, SQL запрос каждый раз динамически формируется заново. И, как следствие, неверно отформатированные данные могут испортить запрос, или даже поменять его, подставив непредусмотренные нами операторы. Собственно, именно в этом и состоит суть инъекций.
Что же нам предлагает серверная обработка плейсхолдеров?
Очень простую вещь: мы вносим в нашу программу такое понятие, как… переменные! Да-да, плейсхолдер — это обычная переменная, которая жёстко прописана в нашем SQL-«скрипте» и не меняется в зависимости от данных. А сами данные едут на сервер отдельно от запроса, и никогда с ним не пересекаются. Только после того, как запрос будет интерпретирован, данные будет использованы уже непосредственно на этапе исполнения.
На практике это выглядит так: при вызове prepare() наш запрос едет на сервер прямо в таком виде — с плейсхолдерами/переменными, сервер его парсит и сигнализирует — «всё окей, готов принимать данные» (ну, или сообщает об ошибке). А затем, при выполнении execute(), на сервер едут уже данные (причём не в текстовом виде, а в бинарном пакете, по структуре напоминающем тот, в котором возвращается результат запроса), и участвуют уже непосредственно в выполнении.
В теории звучит очень заманчиво.
Однако на практике, к сожалению, в имеющихся библиотеках для работы с Mysql в PHP реализация работы с подготовленными выражениями ещё очень далека от идеала.
Достаточно привести такие, к примеру, факты:
  • Такая банальная возможность, как получение строки результата в массив (аналог mysql_fetch_array()), для запросов, использующих подготовленные выражения, была добавлена в mysqli только в версии в 5.3(!). До этой версии библиотека была практически неюзабельна без адовых извращений.
  • То же самое касается задания кодировки соединения в PDO — это стало возможным только в той же 5.3. Во всех предыдущих версиях задать кодировку соединения в PDO было попросту невозможно (что — справедливости ради следует признать — не является большой проблемой при выключенном режиме совместимости, но — дыра-с!).
  • mysqli в некоторых случаях пытается зарезервировать столько памяти, сколько максимально вмещает поле в БД — осторожнее с mediumtext-ами!
Эти факты говорят нам о том, что обе библиотеки до сих пор довольно сырые, и мы можем ожидать от них отсутствия и другого необходимого функционала.
Перечислим основные недостатки имеющихся библиотек

  • многословность
  • отсутствие некоторых полезных плейсхолдеров
  • невозможность получить классический SQL запрос для целей отладки
  • возможные проблемы с производительностью* (этот вопрос будет рассмотрен ниже)
и разберём их подробнее
Многословность.
Возьмём такую, к примеру, востребованную операцию, как получение всех строк результата запроса в двумерный массив. В mysqli до сих пор нет такой функции. Или, скажем, привязывать переменные к плейсхолдерам там можно только отдельной функцией.
В итоге, на получение данных одного запроса в массив у нас уйдёт минимум девять(!) строк кода:
$data  = array();
$query = "SELECT Name, Population, Continent FROM Country WHERE Continent=? ORDER BY Name LIMIT 1";
$stmt->prepare($query);
$stmt->bind_param("s", $continent);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_array(MYSQLI_NUM)) {
  $data[] = $row;
}
Причём большая часть этого кода не несёт никакой смысловой нагрузки, является абсолютно одинаковой для всех выполняемых запросов и при этом повторяется множество раз в сотнях скриптов.
при том, что для получения данных вполне достаточно всего двух строк:
$query = "SELECT Name, Population, Continent FROM Country WHERE Continent=? ORDER BY Name LIMIT 1";
$data  = $db->getAll($query,$continent);
У PDO с этим немного лучше — там догадались разрешить передавать массив в execute() и есть метод fetchAll(). Но всё равно приходится писать множество ненужного однообразного кода для самых простых операций.
В частности — вернёмся к теме нашей статьи — биндинг. Вот есть у меня переменная $_GET['id']; я хочу привязать её к плейсхолдеру. Отлично, я могу это сделать прямо в execute()… но только сделав её массивом. Зачем? Неужели сам драйвер не может сделать это за меня?
Недостаточность функциионала
Ещё одна проблема, которая уже поднималась в комментариях к недавней статье — оператор IN(). Сделать для него подстановки — задача весьма нетривиальная. Хотя казалось бы, именно для таких случаев плейсхолдеры-то как раз и придуманы:
$conts = array('Europe','Africa','Asia','North America');
$query = "SELECT * FROM Country WHERE Continent IN(?) ORDER BY Name LIMIT 1";
$data  = $db->getAll($query,$conts);
Вместо десятков строк хитровыдуманного кода.
Какой вывод мы можем из этого сделать? Надо допиливать.
Невозможность получить SQL запрос
Кто-то скажет, что ему это не нужно, кто-то изворачивается и пишет запрос руками или использует хитрый софт. Будем уважать мнение этих людей, но факт остаётся фактом — функционал вывода готового запроса удобен для отладки, а серверные плейсхолдеры его не позволяют.
Производительность
Обычно апологеты серверных подготовленных выражений напирают на тот факт, что парсинг запроса делается только один раз.
К сожалению, для веб-приложения это не работает. Та копия скрипта, которая выполнила prepare(), выполняет execute() для этого запроса ровно 1 раз и благополучно умирает. А новая копия заново делает prepare. В итоге выходит больше работы там, где хотели сэкономить.
В комментариях к недавней статье мне указали на ещё один потенциальный выигрыш в скорости — кэширование плана запроса. И ведь действительно, даже если мы делаем prepare для каждого запроса, то БД может закэшировать один и тот же запрос для разных данных. И получить план исполнения без парсинга, а простым сравнением строк!
Увы, я не настолько силён во внутреннем устройстве MySQL, чтобы подтвердить или опревергнуть как наличие самого механизма, так и его практическую эффективность.
При этом, насколько мне известно, под большими реальными нагрузками серверные подготовленные выражения проигрывают по скорости стандартным SQL запросам.
В любом случае, тема открытая и ещё ждёт своего исследователя. В конце концов, версии СУБД растут и результаты тестов устаревают.
В общем, мы выяснили, что средства, которые предоставляет нам СУБД и драйверы для работы с ней, оказались не так хороши, какими представлялись в рекламе. И возникает вопрос — а можем ли мы реализовать работу с плейсходерами самостоятельно? И возникает ответ — можем!

Самостоятельная реализация плейсхолдеров

Вообще, на самом деле никто не мешает нам улучшить юзабилити имеющихся библиотек, не заморочиваясь на плейсхолдеры. Скажем, написать обёртку над PDO, которая реализует недостающий функционал, используя при этом собственные плейсхолдеры PDO (или mysqli, как это сделано в библиотеке, опубликованной год назад на Хабре).
Но у нас есть несколько причин рассмотреть самодельные плейсхолдеры:
Во-первых, как мы уже убедились, имеющегося в стандартных библиотеках набора плейсхолдеров явно недостаточно.
Во-вторых, серверные плейсхолдеры по каким-либо причинам могут нам не подойти.
В-третьих, на примере самостоятельной обработки плейсхолдеров мы рассмотрим нюансы корректного форматирования SQL запросов.
Принципы форматирования различных элементов SQL запроса
Реализовать самодельные плейсхолдеры совсем несложно. Примитивный парсер уже встроен в PHP. Единственное, что нам нужно — это научиться отличать различные элементы запроса. Но это очень важный момент, на котором стоит остановиться поподробнее.
Поскольку правила форматирования зависят от типа элемента, нам надо, во-первых, чётко понимать, какой именно элемент запроса мы в него подставляем. А во-вторых, нам надо как-то сообщить эту информацию обработчику плейсходеров.
Для начала давайте определимся, из каких элементов вообще может состоять запрос?
Возьмём, для примера, такой SQL:
INSERT INTO `db`.`table` as `t1` VALUES('string',1,1.5,NOW());
В нём можно выделить три основные группы элементов:
  • собственно элементы языка SQL — операторы, встроенные функции, переменные и пр.
  • идентификаторы (имена баз данных, таблиц и полей)
  • литералы (данные, подставленные непосредственно в запрос) нескольких разных типов.
Нас будут интересовать последние два пункта, поскольку только они требуют специального форматирования.
Теперь подумаем, как сообщить информацию о типе подставляемых данных нашему обработчику. Имеющиеся решения делают это криво (что уже не должно вас удивлять). Варианта тут два: либо тип приходится задавать, вызывая функцию биндинга (что сразу в разы усложняет код), либо не задавать его вовсе, как делает PDO, если передать данные прямиком в execute(). Но совсем без типа обойтись нельзя, и поэтому все переданные в execute данные PDO трактует, как строки. Что приводит к забавным последствиям если PDO работает в режиме совместимости: при попытке передать параметры для LIMIT-а в execute(), PDO вылетит с сообщением об ошибке, в котором можно разглядеть кавычки, которыми честная библиотека обрамила оффсет и лимит!
В общем, нужно другое решение. И оно есть! Но о нём мы поговорим ниже, а пока рассмотрим правила форматирования.
Форматирование идентификаторов
Вообще, правила именования идентификаторов довольно обширные. Но учитывая, что для безопасности мы пользуемся белыми списками, а плейсхолдер — только для форматирования, то достаточно будет этих двух:
  • идентификатор должен быть заключён в обратные одинарные кавычки (backticks)
  • если такая кавычка встречается в имени — она должна быть экранирована удвоением.

function escapeIdent($value)
{
 return "`".str_replace("`","``",$value)."`";
}
Лирическое отступление:
Нужно или не нужно форматировать идентификаторы? Ведь в большинстве случаев это не требуется?
Если запрос пишется руками, то необходимость можно определить на месте: работает запрос — можно не форматировать; вылетает с ошибкой на идентификаторе — надо форматировать.
Если же мы используем плейсхолдер — то есть, добавляем идентификатор в запрос динамически — форматировать надо обязательно, поскольку мы не знаем, какое имя поля будет подставлено в запрос, и, следовательно, требуется ли ему форматирование, или нет. Значит, будем форматировать всё.
Собственно, если говорить об использовании плейсхолдеров в целом, именно последовательное, без исключений, применение правил форматирования и позволяет говорить о гарантированной защите от инъекций и — что немаловажно — ошибок. Ведь форматирование в первую очередь делается для того, чтобы обеспечить синтаксическую корректность запроса. А защита от инъекций является всего лишь побочным эффектом.
Поэтому я предпочитаю употреблять термин «форматирование», а не «эскейпинг»
Форматирование строковых литералов
Самая избитая, казалось бы, тема. Но, как показали обсуждения недавних статей, многие до сих пор путаются если не в понимании, то как минимум — в формулировках. Итак, давайте сформулируем правила форматирования строк в SQL.
  • строка должна быть заключена в кавычки (одинарные или двойные, но поскольку двойные могут быть использованы для идентификаторов, лучше всегда использовать одинарные)
  • в строке должны быть экранированы спецсимволы по списку. Для этого API предоставляет специальную функцию. Для корректной работы этой функции должна быть правильно задана кодировка соединения
при этом
  • эти правила всегда должны применяться вместе, а не только какое-то одно
  • ни одно из них не должно применяться к любым другим данным кроме строк
Вроде бы, простые правила? Но просто поразительно, сколько людей им не следует.
и даже в документации по PHP, в статье про mysql_real_escape_string() написана дичь: «Если не пользоваться этой функцией, то запрос становится уязвимым для взлома с помощью SQL-инъекций. » — как будто если её использовать для чисел или идентификаторов, то это хоть чему-то поможет!
А вот разработчиков PDO можно похвалить — они поступили совершенно логично, выполняя оба правила вместе: функция PDO::quote() делает не половину дела, а всё целиком — эскейпит строку и заключает её в кавычки. Поступим так же и мы:
 function escapeString($value)
{
    return  "'".mysqli_real_escape_string($this->connect,$value)."'";
}
Остаётся только добавить, что «правильно задать кодировку» можно только функциями mysqli_set_charset / mysql_set_charset, а в PDO — в DSN.
Форматирование чисел
Теоретически, в большинстве случаев числа можно форматировать как строки, и тогда задача сведётся к предыдущей. Но есть три проблемы
  • режим STRICT MODE в mysql, который, будучи включённым, в некоторых случаях будет выдавать ошибки при попытке выдать строку за число
  • оператор LIMIT, в котором использование строк не предусмотрено вовсе
  • замечания специалистов по mysql о том, что тип литерала очень важен, и играет роль при планировании и исполнении запроса. Сам я в этом не силён, и буду рад подробному комментарию.
В общем, числа форматировать тоже будем. Тем более, что с помощью плейсхолдеров это не представляет проблемы.
Проблему представляет другое — недостаточноая разрядность встроенных механизмов приведения типов в PHP. Поэтому если вам надо работать с целочисленными значениями, большими чем PHP_INT_MAX, используйте регулярки для валидации. Ну а в обычном случае можно использовать intval().
Числа же с десятичной точкой желательно проверять только регуляркой, поскольку в PHP нет типа данных, аналогичного супер-полезному типу DECIMAL в MySQL.
«Ленивые плейсхолдеры»
Итак, приступим к реализации.
Самый первый вариант, который приходит в голову — это sprintf(). Собственно, подстановочные выражения этого семейства функций и есть настоящие плейсхолдеры. Причём типизованные плейсхолдеры! То есть, то, как именно будут обрабатываться данные, определяет сам плейсхолдер. Это, на самом деле, не такая уж незначительная вещь — ни авторы PDO, ни авторы mysqli так до неё и не додумались. И указать тип подставляемых данных в этих библиотеках можно только вызвав специальную функцию.
В общем, sprintf снимет с нас заботу о любых типах данных кроме строк. Разумеется, sprintf никак не форматирует строки, а выдаёт их как есть. Но нас это не устраивает — у нас есть жёсткие правила форматирования. И это именно правила, а не правило. Как мы помним, эскейпинг — всего лишь пол-дела.
Но поскольку такую ответственную работу, как добавление кавычек, мы не можем отдать на откуп программисту, наш код должен это делать сам. Тупо строковой заменой. Это неидеальное решение, но для учебного кода подойдёт.
Кроме этого мы будем эскейпить все передаваемые в функцию данные. Да, это неправильно. Но вреда от этого не будет, но зато мы сохраним главное преимущество sprintf() — тот факт, что она будет парсить строку за нас.
Почему не будет вреда? Ведь только что я рассказывал, что эскейпить что-либо кроме строк — бесполезно и опасно! Да, если мы больше никак не обрабатываем остальные данные. Но поскольку оставшимися вариантами плейсхолдеров являются только числовые, инъекция через них не пройдёт.
В итоге у нас получилась функция
function query(){
  $query = array_shift($args);
  $query = str_replace("%s","'%s'",$query);
  foreach ($args as $key => $val) {
    $args[$key] = mysql_real_escape_string($val);
  }
  $query = vsprintf($query, $args);
  if (!$query) return FALSE;

  $res = mysql_query($query) or trigger_error("db: ".mysql_error()." in ".$query);
  return $res;
}
которая неплохо справляется со своими обязанностями по подстановке данных в запрос.
причём с защитой от дурака: если мы перепутаем плейсхолдер, и поставим, к примеру, %s вместо %d в операторе LIMIT, то получим ошибку запроса на этапе разработки, но не инъекцию (как это произошло бы в случае эскейпинга).
Разумеется, реализация на основе printf() не лишена недостатков — нaдо следить, чтобы не было лайков, прописанных прямо в запросе, плюс всего три типа плейсхолдеров.
Но для тех, кто ну ооочень не любит ООП, приведу здесь функцию, которую я написал много лет назад и которую можно просто скопировать в конфигурационный файл и начать пользоваться, получив безопасный способ выполнения запросов, ко всему ещё и сокращающий код получения данных в 3-4 раза.
показать код
function dbget() {
  
  $args = func_get_args();
  if (count($args) < 2) {
    trigger_error("dbget: too few arguments");
    return false;
  }
  $mode  = array_shift($args);
  $query = array_shift($args);
  $query = str_replace("%s","'%s'",$query); 

  foreach ($args as $key => $val) {
    $args[$key] = mysql_real_escape_string($val);
  }

  $query = vsprintf($query, $args);
  if (!$query) return false;

  $res = mysql_query($query);
  if (!$res) {
    trigger_error("dbget: ".mysql_error()." in ".$query);
    return false;
  }

  if ($mode === 0) return $res;

  if ($mode === 1) {
    if ($row = mysql_fetch_row($res)) return $row[0];
    else return NULL;
  }

  $a = array();
  if ($mode === 2) {
    if ($row = mysql_fetch_assoc($res)) return $row;
  }
  if ($mode === 3) {
    while($row = mysql_fetch_assoc($res)) $a[]=$row;
  }
  return $a;
}
?>

примеры использования:
$name = dbget(1,"SELECT name FROM users WHERE id=%d",$_GET['id']); 
$user = dbget(2,"SELECT * FROM users WHERE id=%d",$_GET['id']); 

$sql  = "SELECT * FROM news WHERE title LIKE %s LIMIT %d,%d"; 
$news = dbget(3,$sql,"%$_GET[search]%",$start,$per_page); 
Несмотря на некоторую неуклюжесть, эта функция дорога моему сердцу, поскольку написана в строгом следовании известным принципам: и если KISS ей можно поставить в упрёк, то применение её делает код очень и очень DRY.
Но все мы понимаем, что это тупиковая ветвь. И нам нужен полноценный класс.
Увы, объём статьи уже и превысил все разумные размеры, и создание класса надо выносить в отдельный пост. Остановлюсь здесь лишь на нескольких вопросах.
Во-первых, класс наш будет наследником вышеприведённой функции, и служить высокой миссии избавления кода от повторений. Поэтому помимо поддержки плейхолдеров, класс будет предоставлять набор функций-хелперов для получения из БД информации сразу в нужном формате — скаляра, одномерного массива, двумерного массива, индексированного по полю одномерного массива, индексированного по полю двумерного массива.
Во-вторых, для нашего класса мы используем поистине блистательную идею типизованных плейсхолдеров. А заодно поживимся у PDO плейсхолдерами именованными.
Пусть наш плейсхолдер имеет вид
[a-z]:[a-z]*
Например,
i:
или
s:name
В первом случае это будет анонимный плейсхолдер, а во втором — именованный.
первая буква задаёт тип, двоеточие отличает плейсхолдер от других элементов строки, а имя — опционально.
В-третьих, долгожданный плейсхолдер для оператора IN() (для строк)
function createIN($data)
{
 if (!is_array($data))
 {
  throw new E_DB_MySQL_parser("Value for a: type placeholder should be array.");
 }
 if (!$data)
 {
  throw new E_DB_MySQL_parser("Empty array for a: type placeholder.");
 }
 $query = $comma = '';
 foreach ($data as $key => $value)
 {
  $query .= $comma.$this->escapeString($value);
  $comma  = ",";
 }
 return $query;
}
и пара замечаний по нему.
Как можно видеть, парсер выбрасывает исключение на пустой массив. Почему?
Потому что mysql ругнётся на пустой IN(), и выполнять запрос всё равно бесполезно.
Где-то я видел остроумный вариант с implode и кавычками по краям, но так делать нельзя: пустая строка — вполне себе честное значение, но искать её имеет смысл только если она явно была передана в массиве.
DmitryKoterov в своей библиотеке dbSimple эту проблему решает довольно красиво — весь блок с оператором IN заключается в фигурные скобки, и, в случае пустого массива, изымается из запроса целиком. Я не уверен в правильности этого решения, но оно, по крайней мере, весьма остроумно.
Update:
Благодаря идее, которую подали zerkms и david_mz, оказалось, что проблема с пустым массивом решается очень просто!
IN(NULL)
не выдаёт ошибку и всегда возвращает FALSE — идеальное представление для пустого массива.
То есть, вместо него надо просто подставлять NULL:
function createIN($data)
{
 if (!is_array($data))
 {
  throw new E_DB_MySQL_parser("Value for a: type placeholder should be array.");
 }
 if (!$data)
 {
  return 'NULL';
 }
 $query = $comma = '';
 foreach ($data as $key => $value)
 {
  $query .= $comma.$this->escapeString($value);
  $comma  = ",";
 }
 return $query;
}
Первое же исключение я решил оставить, исходя из таких соображений:
Теоретически, мы могли бы превратить скаляр в массив и дальше обрабатывать обычным порядком. Но такие скрытые преобразования типов, хоть и привычны для языка, чреваты логическими ошибками.
В итоге, если массив формируется внутри нашего кода, то эта проверка поможет нам отловить все ошибки формирования на этапе разработки. Ну а если массив приходит извне, то тут тем более нужно кидать исключение.
</update>
Cам же я остановился на варианте с предварительной валидацией массива. В этом мне помогает ещё один метод класса — parse(), который парсит строку с плейсхолдерами, подставляет переданные параметры и выдаёт готовый SQL запрос… или его часть(ведь, в отличие от случая с серверными плейсхолдерами, мы легко можем пропарсить произвольный кусок запроса!). И если первое нам пригодится для отладки, то второе — для случаев, подобных нашему:
if (is_array($array) and $array) {
 $sql .= $db->parse(" AND type IN(a:)",$array);
}
Так же этот метод можно применять для составления многоэтажных условных WHERE:
$w     = array();
$where = '';
if (!empty($_GET['type']))      $w[] = $db->parse("type = s:",    $_GET['type']);
if (!empty($_GET['rooms']))     $w[] = $db->parse("rooms IN (a:)",$_GET['rooms']);
if (!empty($_GET['max_price'])) $w[] = $db->parse("price <= i:",  $_GET['max_price']);
if (count($w)) $where = "WHERE ".implode(' AND ',$w);
$data = $db->getArr("SELECT * FROM table $where LIMIT i:,i:",$start,$per_page);
Вышеупомянутую билиотеку DbSimple, кстати, стоит отметить отдельным пунктом, как пример готовой реализации большей части изложенных здесь идей (в частности, трансляция самописных плейсхолдеров в родные, типизованные плейсхолдеры на 100500 типов и многое другое), отдельно отметив тот факт, что написана она была чуть ли не 10 лет назад, но по какой-то причине не получила широкого распространения.

Сеанс разоблачения суеверий

Поговорим немного о заблуждениях.
Я не знаю другой такой темы в веб-разработке, которая обросла бы подобным количеством заблуждений и суеверий. Разве что тема шаблонизации, хехе.
Судя по всему, механизм здесь такой: с азами защиты от инъекций мы знакомимся очень рано, в самом начале. И заученные правила запоминаются в качестве основ, незыблемых истин. И в дальнейшем мы уж к рассмотрению вопроса не возвращаемся. А надо бы.
Плюс толковой информации в интернете как всегда мало, а самому тестировать и разбираться лень. В итоге, наряду с уже существующими суевериями, в интернет вбрасываются (и подхватываются массами!) новые, не менее непрофессиональные. Попробуем разобраться с некоторыми из них
Функции mysql_* уже давно deprecated в PHP
Грустная тема.
История о том, как лень и невежество хоронят прекрасную библиотеку.
Во-первых, не «функции», а расширение целиком.
Во-вторых, не deprecated, а discouraged, причём не давно, а совсем недавно. Тоже не сахар, но надо быть точнее в формулировках.
В-третьих, никаких реальных причин отказываться от этой прекрасной и стабильной библиотеки нету. И единственная причина, по которой её хотят исключить из языка — не нашлось мейнтейнера, который бы хотел ей заниматься. Ну и плюс негативное общественное мнение, которое сложилось только из-за того, что общество само же не научилось правильно с ней обращаться. Однако против лома не попрёшь, и большие красные предупреждения в мануале говорят нам о том, что с расширения mysql надо срочно переезжать.
Но в этой фразе есть еще один нюанс, очень важный. Из разряда вещей, которые не понимают очень многие начинающие разработчики.
Если говорить именно о «функциях mysql_*», то в коде приложения их действительно быть не должно! Равно как и функций mysqli_*, pdo_* или любых других обращений к «голому» API. Прошу отметить — речь идёт о прикладном коде, а не о коде вообще: просто все обращения к функциям API должны быть упакованы в библиотеки, и в коде приложения нужно обращаться уже к библиотечным функциям. А использование всех этих mysql(i)_query(), и mysql(i)_fetch_array() для исполнения конкретных запросов говорит, увы, о непрофессионализме.
Причина этого, в сущности, очень простая: функции API ужасно избыточны. Для того, чтобы получить строку данных из БД, надо написать десяток строк. И это не учитывая таких необходимых вещей, как обработка ошибок, логирование и профайлинг запросов и многого другого. В итоге, если мы пользуемся функциями API прямо в коде, то он получается, с одной стороны — ужасно избыточным, а с другой… недостаточно функциональным!
Авторы PDO попытались сделать свою библиотеку более юзабельной, но, на мой взгляд, недостаточно. И обращения к PDO надо точно так же инкапсулировать в методы собственного класса.
Ещё один аргумент — сейчас приходится менять mysql_* на что-то другое. В единственном файле библиотеки сделать это куда проще, чем в десятках проектов.
Серебряной пули нет
Как видите — есть. Очень простой набор правил, который несложно реализовать и соблюдать.
Надо сказать, что у меня тоже не сразу получилось. Сначала список не был исчерпывающим, потом — компактным.
Эскейпинг
Здесь целый ворох суеверий. Я надеюсь, что после приведенных выше разъяснений вопросов уже не осталось, но, на всякий случай:
  • «проблема с функцией mysql_real_escape_string в том, что ей вообще пользуются» — это ерунда. Единственная проблема функции mysql_real_escape_string() только в том, что очень многие люди не понимают, для чего она нужна
  • «эскейпинг нужен для входящих данных» — разумеется, это не так. эскейпинг нужен для строк
  • «эскейпинг делает „вредные“ символы „безопасными“» — это полная ерунда. Никаких «вредных» символов не бывает. Грубо говоря, эскейпинг всего лишь экранирует ограничители строк, чтобы они, встретившись в тексте, не разбили строку — вот и всё
  • «эскейпинг в принципе имеет какое-то отношение к защите от инъекций» — ну, вы поняли уже? Эскейпинг нужен для форматирования строк. Причём любых строк, а не только потенциально опасных. А защита обеспечивается уже в качестве побочного эффекта
  • «при использовании подготовленных выражений эскейпингом занимается база» — здесь, проблема, скорее в терминологии. С одной стороны, всё верно — серверные плейсхолдеры обрабатываются базой. Но она не эскейпит данные — это просто не нужно, поскольку данные попросту не попадают в SQL запрос. Забавным исключением здесь является PDO, которая по умолчанию работает в режиме совместимости — то есть, не использует серверные плейсхолдеры, а подставляет данные в запрос сама. Но делает PDO это не на сервере. Плюс, как мы уже знаем, слово «эскейпинг» не является синонимом безопасности. Так что PDO занимается не «эскейпингом», а корректным форматированием данных.

Проверка входящих данных на «опасные» слова и символы
Такие как «UNION», одинарная кавычка и пр. Очень смешной способ защиты. Я, когда вижу на каком-нибудь сайте автора, всерьёз предлагающего очередной её вариант, всегда хочу его спросить — а что было бы, если бы если сам сайт пользовался предлагаемой защитой? Наш горе-изобретатель просто не смог бы опубликовать свой пост, как содержащий те самые «опасные» «стоп-слова».
Универсальная функция защиты данных
Тоже очень распространённое заблуждение среди начинающих пхпшников. «Поставим на входе универсальную функцию, которая будет убивать все „вредные символы“ в поступающих данных и таким образом обезопасим себя от любых инъекций!» — думают они.
Но как мы уже выяснили, «вредных» символов не бывает. Есть только служебные символы, которые имеют определённое значение только в определённом контексте. И совершенно безопасны во всех других случаях.
Также мы выяснили, что в первую очередь нам нужна не безопасность, а корректное форматирование данных. А сделать это можно только тогда, когда мы уже знаем — в каком контексте они будут использоваться. На входе в скрипт мы этого ещё, увы, не знаем. Яркий пример такого подхода представляет собой печально известная директива magic_quotes, ныне, к счастью, изъятая из языка. Обрабатывая совсем не те данные, которые нужно, она создавала иллюзию безопасности, защищая только строки (и только попадающие в скрипт извне).
Так что, пожалуйста, не повторяйте этих ошибок — не форматируйте данные для SQL сразу при попадании их в скрипт.
Кодировки
Тема кодировок относится только к ручному форматированию строк. При обработке плейсхолдеров на сервере никакой проблемы не представляет.
Тема тут старая, «это все придумал Шифлетт в восемнадцатом году» и тогда же Альшанецкий показал, что mysql_real_escape_string() тоже не помогает(!)
Вся фишка в том, что вопреки сказанному в документации, mysql_real_escape_string() не «принимает во внимание кодировку соединения», если только её специально не пнуть. По умолчанию же текущая кодировка — latin1, для которой никакие хитрые настройки не требуются, и mysql_real_escape_string() действует не умнее своей туповатой родственницы mysql_escape_string().
При этом в 2006 году средства сообщить функции mysql_real_escape_string() текущую кодировку в расширении mysql просто не было. С тех пор, впрочем, прогресс сильно ушёл вперёд и средство появилось — функция mysql_set_charset(). Которая и должна использоваться для выставления клиентской кодировки вместо запроса SET NAMES.
В любом случае, инъекция возможна только для некоторых экзотический кодировок. Все однобайтные кодировки и UTF-8 безопасны и так (следствием чего является тот факт, что для них годится и пресловутая addslashes()).
Я не поленился в свое время проверить, как это всё работает. Убедился — работает ровно так, как предсказано.
Под спойлером результаты тестов, если не лень смотреть.
MySQL.

mysql> select version();
+
| version()           |
+
| 5.0.45-community-nt |
+
1 row in set (0.00 sec)

mysql> CREATE TABLE users (
    ->     username VARCHAR(32) CHARACTER SET GBK,
    ->     password VARCHAR(32) CHARACTER SET GBK,
    ->     PRIMARY KEY (username)
    -> );
Query OK, 0 rows affected (0.08 sec)

mysql> insert into users SET username='ewrfg', password='wer44';
Query OK, 1 row affected (0.02 sec)

mysql> insert into users SET username='ewrfg2', password='wer443';
Query OK, 1 row affected (0.03 sec)

mysql> insert into users SET username='ewrfg4', password='wer4434';
Query OK, 1 row affected (0.00 sec)

PHP
<pre><?php
echo "PHP version: ".PHP_VERSION."\n";

mysql_connect();
mysql_select_db("test");
mysql_query("SET NAMES GBK");

$_POST['username'] = chr(0xbf).chr(0x27).' OR username = username /*';
$_POST['password'] = 'guess';

$username = addslashes($_POST['username']);
$password = addslashes($_POST['password']);
$sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);
var_dump($username);
var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());

$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);
$sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);
var_dump($username);
var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());

mysql_set_charset("GBK");
$username = mysql_real_escape_string($_POST['username']);
$password = mysql_real_escape_string($_POST['password']);
$sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
$result = mysql_query($sql) or trigger_error(mysql_error().$sql);
var_dump($username);
var_dump(mysql_num_rows($result));
var_dump(mysql_client_encoding());
Результат
PHP version: 5.3.3
string(29) "ї\' OR username = username /*"
int(3)
string(6) "latin1"
string(29) "ї\' OR username = username /*"
int(3)
string(6) "latin1"
string(30) "\ї\' OR username = username /*"
int(0)
string(3) "gbk"

Характерная деталь, о которой я упоминал ранее:
В PDO до недавнего времени вообще нельзя было выставить кодировку соединения. Функции, аналогичной mysql_set_charset() в PDO нету, а в DSN до версии 5.3 был только муляж параметра charset, который ошибок не выдавал, но и никакую кодировку не выставлял.
Ничего, в сущности, особенного, кроме возможности троллить персонажей, рассказывающих о том, что PDO защищает от всего.
«Уязвимость в LIKE»
Уязвимость в LIKE бывает только одна: применение этого оператора не по назначению.
Почему-то всякие составители статей ужасно любят пугать новичков возможностью подставить в выражение метасимвол. При этом никто не пишет, что если LIKE используется, то метасимвол в любом случае должен быть. Иначе в LIKE нет смысла. То есть, правильным средством борьбы с этой уязвимостью будет не экранирование символов % и _, а неиспользование этой функции там, где нас интересует точное совпадение — для проверки пароля, например. Вот и всё.

Заключение

Хоть целью этой статьи и не является реабилитация расширения mysql, тем не менее я бы хотел отметить, что проблема, как это часто бывает, оказывается не в инструменте, а в руках, которые его держат. И если есть понимание целей и механизмов их достижения, то и с «устаревшим» инструментом можно получить результат не хуже.
Поэтому я старался не насадить одну «религию» вместо другой, разрекламировать новый метод на замену старому, но попытался объяснить, какой цели мы хотим достичь, зачем, и какие есть варианты её достижения.
И если это мне хоть чуточку удалось, я буду считать свою задачу выполненной.
Update: класс для удобной работы с mysql и защиты от SQL инъекций, реализующий изложенные в статье принципы.