"set names"vs mysqli_set_charset - 除了影响mysqli_escape_string之外,它们是否相同?

Pac*_*ier 12 php mysql security encoding libmysql

似乎是常识使用mysql_set_charset/ mysqli::set_charset,而不是直接的MySQL查询的set names.

经常被引用的原因set names是不安全,因为用于mysql_real_escape_string/ 的编码mysqli::real_escape_string只能通过调用mysql_set_charset/ 来设置mysqli::set_charset.(引用的另一个原因是PHP文档说它"不推荐" §.)

但是,set names如果我们使用预备语句mysql_real_escape_string/mysqli::real_escape_string或除了// 之外的其他逃逸方法,使用直接MySQL查询是否安全mysqli_escape_string

除了影响mysql_real_escape_string/ mysqli::real_escape_string/ 的编码, vs/?mysqli_escape_string之间有什么区别吗?set namesmysql_set_charsetmysqli::set_charset

bis*_*hop 6

呼叫SET NAMES连接相当于呼叫set_charset,前提是你既不呼叫get_charset也不呼叫mysql_real_escape_string(和朋友).


当你打电话时set_charset,PHP会做两件事.首先,它调用SET NAMES连接.其次,它会记住你设置的字符集.该状态信息稍后仅在get_charsetmysql_real_escape_string(和朋友)函数中使用.因此,如果您不使用这些功能,那么您可以考虑两者等效.

让我们来源:

  1. Userland功能mysql_set_charsetmysqli_set_charset调用...
  2. 引擎功能mysql_set_character_set调用......
  3. 引擎宏mysqlnd_set_character_set,定义为:

    #define mysqlnd_set_character_set(conn, cs) \ ((conn)->data)->m->set_charset((conn)->data, (cs)))

    并扩展到......

  4. MYSQLND_METHOD(mysqlnd_conn_data, set_charset)其中包含以下代码(编号供讨论,这些不是实际的源代码行):

 1   if (PASS == conn->m->local_tx_start(conn, this_func)) {
 2      char * query;
 3      size_t query_len = mnd_sprintf(&query, 0, "SET NAMES %s", csname);
 4 
 5      if (FAIL == (ret = conn->m->query(conn, query, query_len))) {
 6          php_error_docref(NULL, E_WARNING, "Error executing query");
 7      } else if (conn->error_info->error_no) {
 8          ret = FAIL;
 9      } else {
10           conn->charset = charset;
11      }
12      mnd_sprintf_free(query);
13 
14      conn->m->local_tx_end(conn, this_func, ret);
15   }
Run Code Online (Sandbox Code Playgroud)

如您所见,PHP调用SET NAMES连接本身(第3行).PHP还跟踪刚设置的字符集(第10行).评论进一步讨论了会发生什么conn->charset,但足以说它只是在get_charsetmysql_real_escape_string(和朋友)结束.

因此,如果您不关心这种状态,并且您同意既不使用get_charset也不同意mysql_real_escape_string,那么您可以调用SET NAMES连接本身而不会产生任何不良影响.

顺便说一下,我从来没有这样做,但看起来像编译PHP -DPHP_DEBUG=1将通过各种DBG宏实现大量调试.这可能有助于查看代码如何通过此块.


Ric*_*mes 3

(在这个领域)必须做两件事:

  • 在将引号(和其他字符)放入引号内之前对其进行转义。否则引号会给你带来语法错误。
  • 建立客户端中字节的编码。这样INSERTs/SELECTs将知道如何在写入/读取期间更改字节。

第一个需要转义撇号和双引号,因为这两者都是 MySQL 语法中可接受的字符串引号。然后,转义字符本身需要转义。这 3 个字符对于必须的应用程序来说已经足够了。但是,如果您尝试转义 a BLOB(例如 .jpg),各种控制字符可能会造成麻烦。您最好先转换为十六进制,然后使用UNHEX(), 以避免出现问题。注意:这里没有提到任何有关字符集的内容。如果您不处理 .NET BLOBs,那么您可以使用 PHP 的addslashes().

第二项的目的是说“这个字节流是这样编码的(utf8/latin1/etc)”。它的唯一用途是在CHARACTER SET存储/获取的列和客户端中所需的编码(PHP 等)之间进行转换。不同的语言以不同的方式处理它。对于 PHP:

  • mysql_*-- 不要使用该接口;它已被弃用,并将很快被删除。
  • mysqli_*--mysqli::set_charset(...)
  • PDO--new PDO('...;charset=UTF8', ...)

是否set_charset()对 real_escape_string 做了一些事情?我不知道。但这应该不重要。 SET NAMES显然不能,因为它是一个MySQL命令,并且对PHP一无所知。

htmlentities()是该领域的另一个 PHP 函数。它将 8 位代码转换为&实体。这不应该用于MySQL。它只会掩盖其他问题。仅在涉及 HTML 的某些情况下使用它,而不是 PHP 或 MySQL。

目前唯一合理CHARACTER SETs使用的是 ascii、latin1、utf8 和 utf8mb4。这些在“控制”区域中没有“字符”。Sjis 和其他一些字符集可以。这种对控制字符的混淆可能是 real_escape_string 存在的原因。

结论:

在我看来,您需要两种机制:一种用于转义,另一种用于在客户端中建立编码。他们是分开的。

如果将它们联系在一起,PHP 手册就无法提供任何令人信服的理由来选择一种方法而不是另一种方法。