Поддержка неэмулированных подготовленных операторов из MS SQL Server через PHP в Linux

Резюме

Я пытаюсь использовать подготовленные операторы, чтобы остановить SQL-инъекции, но не могу найти необходимую поддержку, чтобы гарантировать правильную работу.


Сценарий

Я размещаю сайт в Linux, который подключается к Microsoft SQL Server с FreeTDS версии 0.91, в частности, с использованием FreeTDS dblib. Я установил версию tds на 7.4 для подключения к базе данных и использую PHP PDO объект.

Согласно документации FreeTDS, 4.2 не поддерживает подготовленные операторы:

TDS 4.2 имеет ограничения

  • Конечно, только ASCII.
  • RPC не поддерживается.
  • BCP не поддерживается.
  • Поля varchar ограничены 255 символами. Если ваша таблица определяет более длинные поля, они будут усечены.
  • динамические запросы (также называемые подготовленными операторами) не поддерживаются.

Однако нет ничего, что указывало бы на то, что 7.4 не поддерживает подготовленные операторы, что дает мне разумную уверенность, что они, по крайней мере, не вызовут ошибку драйвера.

PDO PHP поддерживает специфические для подключения атрибуты через PDO::setAttribute(). Меня интересует PDO::ATTR_ERRMODE, чтобы установить все ошибки как исключения, и PDO::ATTR_EMULATE_PREPARES, чтобы заставить базу данных выполнять подготовленные операторы, если они совместимы.


Проблема

При проверке соединения получаю следующую ошибку:

Ошибка базы данных: SQLSTATE [IM001]: драйвер не поддерживает эту функцию: драйвер не поддерживает атрибуты настройки

Не имея возможности установить PDO::ATTR_EMULATE_PREPARES, я не могу гарантировать, что база данных действительно выполняет подготовленные операторы, как задумано.

Есть ли способ изменить мой подход или есть альтернативный подход, чтобы гарантировать безопасное выполнение подготовленных операторов на MS SQL Server из Linux?


person Stewart Smith    schedule 12.07.2016    source источник


Ответы (1)


Решение

Используйте ODBC вместо dblib, что обеспечивает полную функциональность PDO. Обратите внимание, что существует две возможные конфигурации ODBC: автономный ODBC и FreeTDS с драйвером ODBC. По моему опыту, чтобы установить набор символов для соединения, это должно быть сделано через FreeTDS с использованием драйвера ODBC, что делает комбинированную конфигурацию предпочтительной.


Настройка ODBC

Я просмотрел множество различных сообщений StackOverflow и различные источники документации в Интернете о том, как правильно установить ODBC. Я взял свое решение из смеси следующих трех ссылок:

Ниже приведен список шагов, которые я использовал для настройки ODBC с использованием FreeTDS в системе на основе Debian.

TDS 8.0 поддерживает подготовленные операторы.

ПРИМЕЧАНИЕ. Не поддерживает SET NAMES a или SET CHARSET a при подключении; наборы символов должны быть определены с использованием комбинированной конфигурации, установив атрибут FreeTDS. Использование автономного драйвера ODBC по умолчанию установило кодировку ASCII, что дало странные результаты. Примеры возможных вопросы.

Установить требуемые пакеты:

sudo apt-get install freetds-bin freetds-common unixodbc tdsodbc php5-odbc

  • freetds-bin предоставляет FreeTDS, а также tsql и isql (используются для отладки позже).
  • freetds-common уже был установлен в системе, но не включает два средства отладки. Установка freetds-bin позже, после определения конфигурации, не вызывает проблем.
  • unixodbc - это драйвер ODBC
  • tdsodbc обеспечивает протокол TDS для ODBC
  • php5-odbc - это модуль php для используемых драйверов ODBC. Обратите внимание, что ваша версия php может отличаться от моей.

Настроить автономный unixODBC

Настройки драйвера ODBC в /etc/odbcinst.ini:

[odbc]
Description     = ODBC driver
Driver          = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
Setup           = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so 
UsageCount      = 1

Создайте общесистемную конфигурацию имени источника данных в /etc/odbc.ini:

[datasourcename]
 Driver         = odbc
 Description    = Standalone ODBC
 Server         = <IP or hostname>
 Port           = <port>
 TDS_Version    = 8.0

Настройте unixODBC и FreeTDS:

Настройки драйвера ODBC в /etc/odbcinst.ini:

[odbc]
Description     = ODBC driver
Driver          = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
Setup           = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so 
UsageCount      = 1

Создайте общесистемную конфигурацию имени источника данных в /etc/odbc.ini:

[datasourcename]
Driver          = FreeTDS_odbc
Description     = Uses FreeTDS configuration settings defined in /etc/freetds/freetds.conf
Servername      = datasourcename
TDS_Version     = 8.0

Добавьте конфигурацию имени источника данных ODBC в FreeTDS в /etc/freetds/freetds.conf:

[datasourcename]
    host = <IP or hostname>
    port = <port>
    client charset = UTF-8
    tds version = 8.0
    text size = 20971520
    encryption = required

ВАЖНО: убедитесь, что файлы odbc доступны для чтения процессу, который будет их читать. Если вы запускаете свой веб-сервер под пользователем www-data, у него должны быть соответствующие разрешения на чтение этих файлов!

Теперь вы можете установить набор символов соединения в freetds.conf и подключиться к базе данных с PDO как

$pdo = new PDO('odbc:datasourcename');

Тестирование:

Используйте tsql, чтобы проверить, что FreeTDS настроен и может подключаться к базе данных.

tsql -S имя источника данных -U имя пользователя -P пароль

Используйте isql, чтобы проверить правильность подключения ODBC.

isql -v имя источника данных имя пользователя пароль

Свяжите ODBC с PHP:

Добавьте модуль ODBC PHP в php.ini, добавив следующее:

extension = odbc.so

Обратите внимание, что ваше php.ini местоположение будет зависеть от того, какой веб-сервер вы используете. Используйте <?php phpinfo(); ?> и просмотрите его через веб-сервер, чтобы узнать его местоположение.

Перезапустите Apache

РЕДАКТИРОВАТЬ: добавлена ​​информация о возможностях набора символов драйвера, поскольку я столкнулся с проблемами с автономной конфигурацией ODBC, когда он игнорировал любую попытку изменить набор символов соединения.

person Stewart Smith    schedule 13.07.2016
comment
Привет, Стюарт, спасибо за ваш пост - я понимаю, что это было много лет назад, но мне остается недоумевать, почему именно эта функция не поддерживается во freetds ›4.2? - person mikey; 27.02.2020
comment
Это было долго. Вполне возможно, что это поддерживается freetds ›4.2. Я оставлю это кому-нибудь другому, чтобы он расследует. - person Stewart Smith; 28.02.2020