从 MS SQL Server 到 Linux 上的 PHP 的非模拟预准备语句支持


Non-Emulated Prepared Statement support from MS SQL Server through PHP on Linux

摘要

我正在尝试使用预准备语句来停止 SQL 注入,但无法找到保证它正常工作所需的支持。


场景

我在Linux上托管了一个站点,该站点连接到具有FreeTDS 0.91版本的Microsoft SQL Server,特别是使用FreeTDS的dblib。我已将数据库连接的tds版本设置为 7.4,并且正在使用 PHP 的 PDO 对象。

根据 FreeTDS 文档,4.2 不支持预准备语句:

TDS 4.2 有限制

  • 当然,只有 ASCII
  • 不支持 RPC。
  • 不支持 BCP。
  • 瓦尔查尔字段限制为 255 个字符。如果您的表定义了 较长的字段,它们将被截断。
  • 不支持动态查询(也称为预准备语句)。

但是,没有任何迹象表明 7.4 不支持准备好的语句,这让我有理由相信它们至少不会引发驱动程序错误。

PHP 的 PDO 通过 PDO::setAttribute() 支持连接特定的属性。我有兴趣PDO::ATTR_ERRMODE将所有错误设置为异常,PDO::ATTR_EMULATE_PREPARES强制数据库在兼容的情况下执行预准备语句。


问题

测试连接时,我收到以下错误:

数据库错误: SQLSTATE[IM001]: 驱动程序不支持此功能: 驱动程序不支持设置属性

如果无法设置PDO::ATTR_EMULATE_PREPARES,我无法保证数据库实际上正在按预期执行准备好的语句。

无论如何可以修改我的方法,或者是否有替代方法,以保证准备好的语句在 Linux 的 MS SQL Server 上安全执行?

解决方案

使用 ODBC 代替 dblib ,这提供了 PDO 的全部功能。请注意,有两种可能的 ODBC 配置:独立 ODBC 和带有 ODBC 驱动程序的 FreeTDS。根据我的经验,要设置连接的字符集,必须使用 ODBC 驱动程序通过 FreeTDS 完成,这使得组合配置更可取。

<小时 />

ODBC 设置

我搜索了许多不同的 StackOverflow 帖子和网络上的各种文档资源,了解如何正确安装 ODBC。我从以下三个参考的混合物中提取了我的解决方案:

  • unixODBC 安装文档
  • FlipperPA 对设置 FreeTDS 的回答(顺便用 ODBC 完成)
  • Benny Hill 对 FreeTDS 的此设置问题的回答

以下是我在基于 Debian 的系统上使用 FreeTDS 配置ODBC的步骤列表。

TDS 8.0 支持预准备语句。

注意:不支持连接上的SET NAMES aSET CHARSET a;字符集需要通过设置 FreeTDS 属性来使用组合配置进行定义。使用独立的ODBC驱动程序将字符集默认为ASCII,这给出了奇数结果。有关可能的问题的示例,请参阅我的另一篇文章。

安装需要包:

sudo apt-get install freetds-bin freetds-common unixodbc tdsodbcPHP 5-ODBC

  • freetds-bin提供 FreeTDS,以及tsqlisql(用于以后的调试)。
  • 系统上已安装freetds-common,但不包括两个调试工具。在定义配置后稍后安装 freetds-bin 不会导致任何问题。
  • unixodbc是 ODBC 驱动程序
  • tdsodbc为 ODBC 提供 TDS 协议
  • php5-odbc是使用 ODBC 驱动程序的 php 模块。请注意,您的 php 版本可能与我的不同。

配置独立 unixODBC

/etc/odbcinst.ini中的 ODBC 驱动程序设置:

[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:

/etc/odbcinst.ini中的 ODBC 驱动程序设置:

[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

/etc/freetds/freetds.conf 中将 ODBC 数据源名称配置添加到 FreeTDS 中:

[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

扩展名 = odbc.so

请注意,您的php.ini位置将取决于您使用的网络服务器。使用<?php phpinfo(); ?>并通过网络服务器查看它以查找其位置。

重启阿帕奇

编辑:添加了有关驱动程序字符集功能的信息,因为我遇到了独立 ODBC 配置的问题,它将忽略任何更改连接字符集的尝试。