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

时间:2014-10-27 20:30:37

标签: php mysql security encoding libmysql

seems to be common knowledge使用mysql_set_charset / mysqli::set_charset代替直接MySQL查询set names

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

但是,如果我们使用预备语句和mysqli::set_charset / set names / other means of escaping之外的mysqli_escape_string,那么使用直接MySQL查询mysql_real_escape_string是否安全?< / p>

除了影响mysqli::real_escape_string / mysql_real_escape_string / mysqli::real_escape_string的编码外,mysqli_escape_stringset names / mysql_set_charset之间是否存在差异?< / p>

4 个答案:

答案 0 :(得分:6)

如果您既不调用SET NAMES也不调用set_charset(和朋友),则在连接上呼叫get_charset等同于呼叫mysql_real_escape_string < / p>

当你致电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) contains以下代码(编号供讨论,这些不是实际的源代码行号):
  5.  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   }
    

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

    因此,如果您不关心此状态,并且您同意不同时使用get_charsetmysql_real_escape_string,那么您可以在连接本身上调用SET NAMES而不会产生任何不良影响

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

答案 1 :(得分:3)

必须做两件事(在这方面):

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

第一个需要转义撇号和双引号,因为这两个都是MySQL语法中字符串的可接受引号。然后,转义字符本身需要转义。这3个字符足以满足必须的应用。但是,如果您试图转义BLOB(例如.jpg),则各种控制字符可能会导致问题。您最好转换为十六进制,然后使用UNHEX(),以避免出现问题。注意:这里没有提到任何关于字符集的内容。如果您不处理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手册未能提供任何令人信服的理由选择一种方法而不是另一种方法。

答案 2 :(得分:1)

mysql:不推荐使用整个界面,所以根本不使用任何一个界面(PHP 7删除了界面)。

mysqli(和PDO)已准备好使用var myApp = angular.module('myApp', []); myApp.controller('mainController', function ($scope, $http) { $http.get('/Home/GetUser') .then(function (response) { $scope.users = response.data; $scope.itmNo = response.length; }) .catch(function (e) { console.log("error", e); throw e; }) .finally(function () { console.log("This finally block"); }); $http.get('/Home/GetSupervisor') .then(function (response) { $scope.supervisors = response.data; }) .catch(function (e) { console.log("error", e); throw e; }) .finally(function () { console.log("This finally block"); }); 不需要(也不想要)的语句。 - &GT;因此,如果您只使用mysqli和预处理语句:不用担心如何设置字符集。

因为你关心安全性:我认为不使用预准备语句没什么意义。

一旦你使用了mysqli的预处理语句,唯一的方法是使用real_escape_string,因为你不能再在一个字符串中连接多个sql语句。

因此,了解差异的问题至多是学术性的,与现实生活无关。

总结:

  • mysql:根本不用。

  • mysqli:使用预处理语句,因此使用$mysqli->set_charset()方法。另外:一旦使用预准备语句,您将不再需要real_escape_string。

  • 或 - 当然 - 使用PDO及其方法。

答案 3 :(得分:1)

SET NAMES ...是一个便利别名:

  

SET NAMES 'charset_name'声明等同于这三个声明   语句:

SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = charset_name;
     

将character_set_connection设置为charset_name也会隐式设置   collat​​ion_connection为charset_name的默认排序规则。

...为MySQL Server提供当前连接所需的所有text-encoding information。到目前为止一切都很好。

但PHP也参与其中,它不会从这里学到任何东西,因为它基本上是一个随机的用户查询。由于显而易见的性能原因,PHP不会做两件事:

  • 扫描发送到服务器的所有用户查询,以检测对SET NAMES的调用。
  • 每次需要处理时,向MySQL询问相关指令的当前值。

简而言之:此方法通知服务器但不通知客户端。但是,专用的PHP函数可以做到这两点。