PHP mysql_real_escape_string() 是否保护数据库名称?

发布于 2024-08-16 17:16:25 字数 536 浏览 0 评论 0原文

我知道 mysql_real_escape_string()
在以下字符前面加上反斜杠:\x00、\n、\r、\、'、" 和 \x1a

我知道这如何保护查询免于注入类似 where 子句中的变量之类的内容。但这里有一个我不确定的场景of:

$query = "SELECT * FROM $db WHERE 1";

如果 $db 取自用户输入,则用户可以插入如下内容:
$db = '真实数据库 WHERE 1;从 RealDatabase 删除 WHERE 1;从真实数据库中选择';

根据我的理解, mysql_real_escape_string() 不会影响这个字符串, 进行最终查询: $query = "SELECT * FROM RealDatabase WHERE 1; DELETE FROM RealDatabase WHERE 1; SELECT FROM RealDatabase WHERE 1";

这将删除数据库。是否还有我不知道的另一级保护?

I know that mysql_real_escape_string()
prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a

I know how this protects a query from injection into something like a variable in a where clause. But here's a scenario I am unsure of:

$query = "SELECT * FROM $db WHERE 1";

If $db is taken from a user input, then the user could insert something like:
$db = 'RealDatabase WHERE 1; DELETE FROM RealDatabase WHERE 1; SELECT FROM RealDatabase';

From my understanding, mysql_real_escape_string() would not affect this string,
making the final query:
$query = "SELECT * FROM RealDatabase WHERE 1; DELETE FROM RealDatabase WHERE 1; SELECT FROM RealDatabase WHERE 1";

which would delete the database. Is there another level of protection I am unaware of?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

迷迭香的记忆 2024-08-23 17:16:25

您正在寻找的保护级别由反引号提供:

"SELECT * FROM `$db` WHERE 1";

反引号用于限定标识符,否则可能会产生歧义(即MySQL 保留字),如果您接受用户输入或具有可变名称的列或数据库,您绝对应该使用反引号,否则我可以保证您会遇到麻烦未来。例如,如果您有一个系统,其中使用一些用户输入创建了临时字段名称,但结果该字段最终被命名为 update ,该怎么办?

"SELECT field1,field2,update FROM table;"

它惨遭失败。但是:

"SELECT `field`,`field2`,`update` FROM table"

效果很好。 (这实际上是我几年前开发的一个存在此问题的系统的真实示例)。

这解决了您输入错误 SQL 的问题。例如,以下查询将仅返回“未知列”错误,其中 test; DROP TABLE test 是注入的攻击代码:

"SELECT * FROM `test; DROP TABLE test`;"

但要小心:带反引号的 SQL 注入仍然是可能的!

例如,如果您的 $db 变量包含以下数据: 里面有一个反引号,你仍然可以以正常的方式注入一些SQL。如果您使用变量数据作为数据库和字段名称,则应在将其放入语句之前去掉所有反引号,然后在放入语句后使用反引号对其进行限定。

$db = str_replace('`','',$db);
$sql = "SELECT * FROM `$db` WHERE 1";

我使用一个数据库包装器,它具有用于清理数据和清理数据库标识符的单独功能,这就是后者所做的:)

The level of protection you are looking for is supplied by backticks:

"SELECT * FROM `$db` WHERE 1";

Backticks are used to qualify identifiers that could otherwise be ambiguous (ie. MySQL reserved words), and if you are accepting user input or have variably-named columns or databases, you absolutely should use backticks, or I can promise that you will run into trouble in the future. For example, what if you had a system where a temporary field name was created with some user input, only it turned out the field ended up being named update?

"SELECT field1,field2,update FROM table;"

It fails miserably. However:

"SELECT `field`,`field2`,`update` FROM table"

works just fine. (This is actually a real example from a system I worked on a few years ago that had this problem).

This solves your problem in terms of putting in bad SQL. For instance, the following query will simply return an "unknown column" error, where test; DROP TABLE test is the injected attack code:

"SELECT * FROM `test; DROP TABLE test`;"

Be careful though: SQL Injection is still possible with backticks!

For instance, if your $db variable contained data that had a backtick in it, you could still inject some SQL in the normal way. If you're using variable data for database and field names, you should strip it of all backticks before putting it into your statement, and then qualifying it with backticks once inside.

$db = str_replace('`','',$db);
$sql = "SELECT * FROM `$db` WHERE 1";

I utilize a database wrapper which has separate functions for sanitizing data and sanitizing database identifiers, and this is what the latter does :)

握住你手 2024-08-23 17:16:25

您确实应该考虑绑定 SQL 查询。

这将基本上保护您免受所有 SQL 注入的影响。归结为:(

摘自 PHP.net)

$stmt = mssql_init('NewUserRecord');

// Bind the field names
mssql_bind($stmt, '@username',  'Kalle',  SQLVARCHAR,  false,  false,  60);

// Execute
mssql_execute($stmt);

PHP 基本上支持所有数据库上的绑定查询。哦,当然,您仍然应该清理所有输入和内容。输出(显示)。

更多信息:
- http://php.net/manual/en/function.mssql-bind .php

You should really look into binding your SQL queries.

This will protect you from basically all SQL injection. It boils down to this:

(taken from PHP.net)

$stmt = mssql_init('NewUserRecord');

// Bind the field names
mssql_bind($stmt, '@username',  'Kalle',  SQLVARCHAR,  false,  false,  60);

// Execute
mssql_execute($stmt);

And PHP has support for binded queries on basically all databases. Oh and of course you should still sanitize all input & output(display).

More info:
- http://php.net/manual/en/function.mssql-bind.php

心凉怎暖 2024-08-23 17:16:25

不,mysql_real_escape_string 不会在这里帮助你。该函数不是上下文敏感的(不可能,因为它没有任何上下文),这是一个完全不同的威胁模型。

您需要去验证该表是否存在,而不是将用户输入的表名直接发送到服务器。最好的解决方案是使用服务器端数组/查找表,其中包含允许使用的表名称。如果他们尝试使用不存在的东西,那就不要让他们使用。

如果您确实需要所有桌子,那么您可以直接询问服务器“您有哪些桌子?”并运行它的输出(可以选择将其缓存一段时间,以防止每次都询问服务器) - 但很可能,最终您将拥有一个您不希望再查看的表,然后您需要无论如何都要使用数组,所以就继续这样做吧。

No, mysql_real_escape_string isn't going to help you here. The function is not context-sensitive (it can't be, because it doesn't HAVE any context), and this is a completely different threat model.

You need to go and validate that the table exists, without sending the user-inputted table name directly to the server. The best solution is to use a server-side array/look-up table containing the table names they are allowed to use. If they try to use something that's not in there, then don't let them.

If you really need ALL of the tables, then you can just ask the server "what tables do you have?" and run through it's output (optionally caching it for some period of time to prevent asking the server every time) - but chances are, eventually you'll have a table that you don't want then to poke around in, and then you need to use the array thing anyway, so just go ahead and do that.

若沐 2024-08-23 17:16:25

您可以创建一个单独的数据库名称和 ID 表,而不是在 get 查询中插入数据库名称。然后仅将 id 附加到查询中。然后您可以查找该 id 对应的数据库名称并使用它。然后,您可以确保收到的 id 是数字 (is_numeric),并且您还可以确定用户可以仅从列表中的数据库中进行选择。

(此外,这将阻止用户找到数据库名称,并可能在站点上的 SQL 注入中的其他位置使用它们。)

使用第一种方法,您可以在查询中使用数据库名称之前解析数据库名称,并确保它不包含空格。

Instead of inserting the database name in the get query you can make a separate table of database names and ids. Then append only the id to the query. Then you can look up the corresponding database name for that id and use that. You can then make sure that the id received is numeric (is_numeric) and you can also be certain that the user can only choose from the databases that are in your list.

(Additionally this will prevent users from finding out names of databases and possibly use them elsewhere in an SQL injection on your site.)

Using the first method you parse the database name before using it in your query and make sure it contains no spaces.

意犹 2024-08-23 17:16:25

由于表名不接受空格字符,因此只需将其删除即可。这将使上面的$DB RealDatabaseWHERE1;DELETEFROMRealDatabase....。这将使查询无效,但可以防止该缺陷。

如果你想防止这种“hackish”的事情,只需执行 explode(' ', $db) 然后获取结果数组的 [0]。这将得到第一部分(RealDatabase),仅此而已。

Since table names do not accept whitespace characters, just strip them out. That would make the above $DB RealDatabaseWHERE1;DELETEFROMRealDatabase..... Such would invalidate the query, but prevent the flaw.

If you want to prevent this kind of 'hackish' things, just do explode(' ', $db) then get the result array's [0]. That would get the first part (RealDatabase) and nothing else.

只想待在家 2024-08-23 17:16:25

最好在使用有问题的数据时使用它。如果您自己指定表并且没有篡改的空间,则无需转义它。如果您的用户正在决定任何可能作为查询运行的内容,请转义它。

Its just best to use it any time that you have questionable data being used. If you are specifying the table yourself and there's no room for tampering, there's no need to escape it. If your users are deciding anything that could potentially get run as a query, escape it.

多情出卖 2024-08-23 17:16:25

如果您确实必须对数据库使用从用户获取的数据(糟糕糟糕糟糕),那么请使用以下编码风格...

$realname = '';
switch ($_GET['dbname']){
    case 'sometoken' : $realname = 'real_name'; break;
    case 'sometoken1' : $realname = 'real_name1'; break;
    case 'sometoken2' : $realname = 'real_name2'; break;
    case 'sometoken3' : $realname = 'real_name3'; break;
    case 'sometoken4' : $realname = 'real_name4'; break;
    case default : die ('Cheeky!!!');
}

$query = "SELECT * FROM `{$realname}` WHERE 1";

或者...

$realname = $tablenames[$_GET['dbname']];

if (!$realname)
    die ('Cheeky!!!');

使用这两种方式或一些类似的编码将保护您的输入免受意外值的影响。

这也意味着用户永远无法看到他们可以从中推断信息的真实表或数据库名称。

确保首先检查 $_GET['dbname'] 的内容以确保其有效,否则将发出警告。

我仍然说这是一个非常糟糕的设计,它让人想起允许用户提供文件名并将其传递给 I/O 函数而不进行检查。考虑起来太不安全了。

安全太重要了,不能让懒惰统治。

If you really really must use a get from the user (bad bad bad) for your database then use the following style of coding...

$realname = '';
switch ($_GET['dbname']){
    case 'sometoken' : $realname = 'real_name'; break;
    case 'sometoken1' : $realname = 'real_name1'; break;
    case 'sometoken2' : $realname = 'real_name2'; break;
    case 'sometoken3' : $realname = 'real_name3'; break;
    case 'sometoken4' : $realname = 'real_name4'; break;
    case default : die ('Cheeky!!!');
}

$query = "SELECT * FROM `{$realname}` WHERE 1";

or alternatively ...

$realname = $tablenames[$_GET['dbname']];

if (!$realname)
    die ('Cheeky!!!');

Using these 2 ways or some similar coding will protect your input from unexpected values.

It also means the user never gets to see the real table or database names which they may be able to infer information from.

Make sure you check the content of $_GET['dbname'] to make sure it's valid first otherwise warnings will be issued.

I still say this is a very bad design, it is reminiscent of allowing users to provide a filename and passing that through to I/O functions without a check. It simply too unsafe to consider.

Security is too important to let laziness rule.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文