通过 Bash 脚本转义 MYSQL 命令行

发布于 2024-10-06 09:34:58 字数 751 浏览 8 评论 0原文

PHP 有 mysql_real_escape_string() 来正确转义任何可能导致问题的字符。为 BASH 模拟此功能的最佳方法是什么?

无论如何,有没有使用 bash 做准备好的 mysql 语句?这似乎是最好的办法。

我的大多数变量不会(不应该)有特殊字符,但是我给用户完全自由的密码。它可能包含像 ' 和 " 这样的字符。

我可能正在执行多个 SQL 语句,因此我想要制作一个接受参数然后运行该语句的脚本。这是我到目前为止所拥有的:

doSQL.sh:

#!/bin/sh

SQLUSER="root"
SQLPASS="passwor339c"
SQLHOST="localhost"

SQL="$1"
SQLDB="$2"


if [ -z "$SQL" ]; then echo "ERROR: SQL not defined"; exit 1; fi
if [ -z "$SQLDB" ]; then SQLDB="records"; fi

echo "$SQL" | mysql -u$SQLUSER -p$SQLPASS -h$SQLHOST $SQLDB

和一个示例使用上述命令:

example.sh:

PASSWORD=$1
doSQL "INSERT INTO active_records (password) VALUES ('$PASSWORD')"

显然,如果密码密码中包含单引号,则这会失败。

PHP has mysql_real_escape_string() to correctly escape any characters that might cause problems. What is the best way to mimic this functionality for BASH?

Is there anyway to do prepared mysql statements using bash? This seems to be the best way.

Most of my variables won't (shouldn't) have special characters, however I give the user complete freedom for their password. It may include characters like ' and ".

I may be doing multiple SQL statements so I'll want to make a script that takes in parameters and then runs the statement. This is what I have so far:

doSQL.sh:

#!/bin/sh

SQLUSER="root"
SQLPASS="passwor339c"
SQLHOST="localhost"

SQL="$1"
SQLDB="$2"


if [ -z "$SQL" ]; then echo "ERROR: SQL not defined"; exit 1; fi
if [ -z "$SQLDB" ]; then SQLDB="records"; fi

echo "$SQL" | mysql -u$SQLUSER -p$SQLPASS -h$SQLHOST $SQLDB

and an example using said command:

example.sh:

PASSWORD=$1
doSQL "INSERT INTO active_records (password) VALUES ('$PASSWORD')"

Obviously this would fail if the password password contained a single quote in it.

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

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

发布评论

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

评论(10

红衣飘飘貌似仙 2024-10-13 09:34:58

在 Bash 中,printf 可以为您进行转义:

$ a=''\''"\;:#[]{}()|&^$@!?, .<>abc123'
$ printf -v var "%q" "$a"
$ echo "$var"
\'\"\\\;:#\[\]\{\}\(\)\|\&\^\$@\!\?\,\ .\<\>abc123

我将让您决定这是否足够激进。

In Bash, printf can do the escaping for you:

$ a=''\''"\;:#[]{}()|&^$@!?, .<>abc123'
$ printf -v var "%q" "$a"
$ echo "$var"
\'\"\\\;:#\[\]\{\}\(\)\|\&\^\$@\!\?\,\ .\<\>abc123

I'll leave it to you to decide if that's aggressive enough.

貪欢 2024-10-13 09:34:58

这似乎是一个使用错误工具完成工作的典型案例。

要在 bash 中实现 mysql_real_escape_string() 完成的转义,您还有大量工作要做。请注意,mysql_real_escape_string() 实际上将转义委托给 MySQL 库,该库会考虑连接和数据库字符集。它之所以被称为“真实”,是因为它的前身 mysql_escape_string() 没有考虑字符集,并且可能被欺骗注入 SQL。

我建议使用具有 MySQL 库的脚本语言,例如 Ruby、Python 或 PHP。

如果您坚持使用 bash,请使用 MySQL 准备的语句语法。

This seems like a classic case of using the wrong tool for the job.

You've got a lot of work ahead of you to implement the escaping done by mysql_real_escape_string() in bash. Note that mysql_real_escape_string() actually delegates the escaping to the MySQL library which takes into account the connection and database character sets. It's called "real" because its predecessor mysql_escape_string() did not take the character set into consideration, and could be tricked into injecting SQL.

I'd suggest using a scripting language that has a MySQL library, such as Ruby, Python, or PHP.

If you insist on bash, then use the MySQL Prepared Statements syntax.

简单爱 2024-10-13 09:34:58

无论您使用什么引号,都无法逃脱以下构造:

function quoteSQL() {
    printf "FROM_BASE64('%s')" "$(echo -n "$1" | base64 -w0 )"
}

PASSWORD=$1
doSQL "INSERT INTO active_records (password) VALUES ($(quoteSQL "$PASSWORD"));"

# I would prefer piping
printf 'INSERT INTO active_records (password) VALUES (%s);\n' $(quoteSQL "$PASSWORD") | doSQL

There is no escape from the following construct, no matter what quotes you use:

function quoteSQL() {
    printf "FROM_BASE64('%s')" "$(echo -n "$1" | base64 -w0 )"
}

PASSWORD=$1
doSQL "INSERT INTO active_records (password) VALUES ($(quoteSQL "$PASSWORD"));"

# I would prefer piping
printf 'INSERT INTO active_records (password) VALUES (%s);\n' $(quoteSQL "$PASSWORD") | doSQL
花间憩 2024-10-13 09:34:58

mysql_real_escape_string() 当然,仅转义要引用的单个字符串文字,而不是整个语句。您需要清楚该字符串在语句中的用途。根据 MySQL 手册中关于字符串文字的部分,对于插入字符串字段时,您只需要转义单引号和双引号、反斜杠和 NUL。但是,bash 字符串不能包含 NUL,因此以下内容应该足够了:

#escape for MySQL single string
PASSWORD=${PASSWORD//\\/\\\\}
PASSWORD=${PASSWORD//\'/\\\'}
PASSWORD=${PASSWORD//\"/\\\"}

如果您将在 LIKE 之后使用该字符串,您可能还需要转义 % 和<代码>_。

准备好的陈述是另一种可能性。并确保您不在 bash 中使用 echo -e

另请参阅 https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet

mysql_real_escape_string() of course only escapes a single string literal to be quoted, not a whole statement. You need to be clear what purpose the string will be used for in the statement. According to the MySQL manual section on string literals, for inserting into a string field you only need to escape single and double quotation marks, backslashes and NULs. However, a bash string cannot contain a NUL, so the following should suffice:

#escape for MySQL single string
PASSWORD=${PASSWORD//\\/\\\\}
PASSWORD=${PASSWORD//\'/\\\'}
PASSWORD=${PASSWORD//\"/\\\"}

If you will be using the string after a LIKE, you will also probably want to escape % and _.

Prepared statements are another possibility. And make sure you don't use echo -e in your bash.

See also https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet

凉栀 2024-10-13 09:34:58

这将转义撇号

a=$(echo "$1" | sed s/"'"/"\\\'"/g)

,但请注意 mysql_real_escape_string 也会转义 \x00、\n、\r、\、" 和 \x1a。为了完全安全,请务必转义这些。

例如,要转义 \x00:

a=$(echo "$1" | sed s/"\x00"/"\\\'"/g)

稍加努力,您就可以可能使用一个 sed 命令来转义这些。

This will escape apostrophes

a=$(echo "$1" | sed s/"'"/"\\\'"/g)

Please note though that mysql_real_escape_string also escapes \x00, \n, \r, \, " and \x1a. Be sure to escape these for full security.

To escape \x00 for example:

a=$(echo "$1" | sed s/"\x00"/"\\\'"/g)

With a bit of effort you can probably escape these using one sed command.

猥琐帝 2024-10-13 09:34:58

当然,为什么不直接使用真实的东西呢?

脚本,可以在任何地方,例如
〜/scripts/mysqli_real_escape.php

#!/bin/php
<?php

$std_input_data = '';
$mysqli             = new mysqli('localhost', 'username', 'pass', 'database_name');

if( ftell(STDIN) !== false  )       $std_input_data = stream_get_contents(STDIN);
if( empty($std_input_data)  )       exit('No input piped in');
if( mysqli_connect_errno( ) )       exit('Could not connect to database');

fwrite  (   STDOUT, 
            $mysqli->real_escape_string($std_input_data) 
        );

exit(0);

?>

接下来,从 bash 终端运行:

chmod +x ~/script/mysqli_real_escape.php`
ln -s ~/script/mysqli_real_escape.php /usr/bin/mysqli_real_escape

一切就绪!现在您可以在 bash 脚本中使用 mysqli_real_escape

#!/bin/bash
MyString="stringW@#)*special characters"
MyString="$(printf "$MyString" | mysqli_real_escape )"

注意:据我了解,使用 "$(cmd ..."$var")" 进行命令替换优于使用反引号。然而,由于不需要进一步的嵌套,所以两者都应该没问题。

进一步注意:在命令替换 "$(...)" 内部时,会创建一个新的引用上下文。这就是变量周围的引号不会弄乱字符串的原因。

Sure, why not just use the real thing?

A script, anywhere, such as
~/scripts/mysqli_real_escape.php

#!/bin/php
<?php

$std_input_data = '';
$mysqli             = new mysqli('localhost', 'username', 'pass', 'database_name');

if( ftell(STDIN) !== false  )       $std_input_data = stream_get_contents(STDIN);
if( empty($std_input_data)  )       exit('No input piped in');
if( mysqli_connect_errno( ) )       exit('Could not connect to database');

fwrite  (   STDOUT, 
            $mysqli->real_escape_string($std_input_data) 
        );

exit(0);

?>

Next, run from bash terminal:

chmod +x ~/script/mysqli_real_escape.php`
ln -s ~/script/mysqli_real_escape.php /usr/bin/mysqli_real_escape

All set! Now you can use mysqli_real_escape in your bash scripts!

#!/bin/bash
MyString="stringW@#)*special characters"
MyString="$(printf "$MyString" | mysqli_real_escape )"

Note: From what I understand, command substitution using "$(cmd ..."$var")" is preferred over using backticks. However, as no further nesting would be needed either should be fine.

Further Note: When inside command substitution, "$(...)", a new quote context is created. This is why the quotes around variables do not screw up the string.

旧话新听 2024-10-13 09:34:58

我就是这样做的,其中 my-file.txt 包含空格、换行符和引号:

IFS='' content=$(cat my-file.txt)
mysql <flags> -e "update table set column = $(echo ${content@Q} | cut -c 2-) where something = 123"

This is how I did it, where my-file.txt contains spaces, new lines and quotes:

IFS='' content=$(cat my-file.txt)
mysql <flags> -e "update table set column = $(echo ${content@Q} | cut -c 2-) where something = 123"
俯瞰星空 2024-10-13 09:34:58

以下是我编写的几个 Bash 函数,它们被分组到一个库中。

它提供了正确引用/转义字符串和标识符的方法:

##### db library functions #####

# Executes SQL Queries on localhost's MySQL server
#
# @Env
# $adminDBUser: The database user
# $adminDBPassword: The database user's password
#
# @Params
# $@: Optional MySQL arguments
#
# @Output
# >&1: The MySQL output stream
db::execute() {
  # Uncomment below to debug
  #tee --append debug.sql |
    mysql \
      --batch \
      --silent \
      --user="${adminDBUser:?}" \
      --password="${adminDBPassword:?}" \
      --host=localhost \
      "$@"
}

# Produces a quoted string suitable for inclusion in SQL statements.
#
# @Params
# $1: The string to bo quoted
#
# @Output
# >&1: The quoted identifier suitable for inclusion in SQL statements
db::quoteString() {
  local -- string="${1:?}"
  local -- bas64String && bas64String=$(printf %s "${string}" | base64)
  db::execute <<< "SELECT QUOTE(FROM_BASE64('${bas64String}'));"
}

# Produces a quoted identifier suitable for inclusion in SQL statements.
#
# @Params
# $1: The identifier to bo quoted
#
# @Output
# >&1: The quoted identifier suitable for inclusion in SQL statements
db::quoteIdentifier() {
  local -- identifier="${1:?}"
  local -- bas64Identifier && bas64Identifier=$(printf %s "${identifier}" | base64)
  db::execute <<< "SELECT sys.quote_identifier(FROM_BASE64('${bas64Identifier}'))"
}

Here are a couple Bash functions I wrote, grouped into a library.

It provides methods for proper quoting/escaping strings and identifiers:

##### db library functions #####

# Executes SQL Queries on localhost's MySQL server
#
# @Env
# $adminDBUser: The database user
# $adminDBPassword: The database user's password
#
# @Params
# $@: Optional MySQL arguments
#
# @Output
# >&1: The MySQL output stream
db::execute() {
  # Uncomment below to debug
  #tee --append debug.sql |
    mysql \
      --batch \
      --silent \
      --user="${adminDBUser:?}" \
      --password="${adminDBPassword:?}" \
      --host=localhost \
      "$@"
}

# Produces a quoted string suitable for inclusion in SQL statements.
#
# @Params
# $1: The string to bo quoted
#
# @Output
# >&1: The quoted identifier suitable for inclusion in SQL statements
db::quoteString() {
  local -- string="${1:?}"
  local -- bas64String && bas64String=$(printf %s "${string}" | base64)
  db::execute <<< "SELECT QUOTE(FROM_BASE64('${bas64String}'));"
}

# Produces a quoted identifier suitable for inclusion in SQL statements.
#
# @Params
# $1: The identifier to bo quoted
#
# @Output
# >&1: The quoted identifier suitable for inclusion in SQL statements
db::quoteIdentifier() {
  local -- identifier="${1:?}"
  local -- bas64Identifier && bas64Identifier=$(printf %s "${identifier}" | base64)
  db::execute <<< "SELECT sys.quote_identifier(FROM_BASE64('${bas64Identifier}'))"
}
金橙橙 2024-10-13 09:34:58

简短回答:使用 \ (转义字符)。

如果您的值具有美元字符,例如: $p123$hello 那么在 bash 中您可以通过 \ 转义它。那么字符串将是 \$p123\$hello

在我的例子中,我已经像这样更改密码:

mysql> update users set password='\$p123\$hello' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Short answer: use \ (escape character).

If your value has dollar character, for example: $p123$hello then in bash you may escape this by \. Then string will be \$p123\$hello

In my case for example i have been change password like this:

mysql> update users set password='\$p123\$hello' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
自控 2024-10-13 09:34:58

这将起作用:

echo "John O'hara"  | php -R 'echo addslashes($argn);'

将其传递给变量:

name=$(echo "John O'hara"  | php -R 'echo addslashes($argn);')

This will work:

echo "John O'hara"  | php -R 'echo addslashes($argn);'

To pass it to a variable:

name=$(echo "John O'hara"  | php -R 'echo addslashes($argn);')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文