mysql_real_escape_string equivalent for Golang

huangapple go评论76阅读模式
英文:

mysql_real_escape_string equivalent for Golang

问题

我可以帮你翻译以下内容:

我想转义数据库查询的一部分值,但是我不能使用参数化查询。

Go语言是否有类似于PHP的mysql_real_escape_string的函数,可以用来转义查询的值?

英文:

I would like to escape a value that makes up part of a database query, but I can't use parameterized queries.

Does Go have an equivalent of PHP's mysql_real_escape_string I can use to escape the query value?

答案1

得分: 9

我自己想出了一个解决方案来创建这个函数。希望对某人有用。

func MysqlRealEscapeString(value string) string {
    replace := map[string]string{"\\":"\\\\", "'":`\'`, "\
func MysqlRealEscapeString(value string) string {
    replace := map[string]string{"\\":"\\\\", "'":`\'`, "\\0":"\\\\0", "\n":"\\n", "\r":"\\r", `"`:`\\"`, "\x1a":"\\Z"}

    for b, a := range replace {
        value = strings.Replace(value, b, a, -1)
    }
    
    return value;
}
"
:"\\\
func MysqlRealEscapeString(value string) string {
    replace := map[string]string{"\\":"\\\\", "'":`\'`, "\\0":"\\\\0", "\n":"\\n", "\r":"\\r", `"`:`\\"`, "\x1a":"\\Z"}

    for b, a := range replace {
        value = strings.Replace(value, b, a, -1)
    }
    
    return value;
}
"
, "\n":"\\n", "\r":"\\r", `"`:`\\"`, "\x1a":"\\Z"}
for b, a := range replace { value = strings.Replace(value, b, a, -1) } return value; }
  1. MysqlRealEscapeString是不正确的,因为下面的测试用例将失败
func TestEscape(t *testing.T) {
    mysqlEscapeList := map[string]string{
        "\\": "\\\\", "'": `\'`, "\
func TestEscape(t *testing.T) {
    mysqlEscapeList := map[string]string{
        "\\": "\\\\", "'": `\'`, "\\0": "\\\\0", "\n": "\\n", "\r": "\\r", `"`: `\"`, "\x1a": "\\Z"}

    for old, want := range mysqlEscapeList {
        testEscape(t, old, want)
    }
    testEscape(t, `<p>123</p><div><img width="1080" />`, `<p>123</p><div><img width=\"1080\" />`)
}
func testEscape(t *testing.T, origin, want string) {
    escaped := MysqlRealEscapeString(origin)
    assert.Equal(t, want, escaped)
}
"
: "\\\
func TestEscape(t *testing.T) {
    mysqlEscapeList := map[string]string{
        "\\": "\\\\", "'": `\'`, "\\0": "\\\\0", "\n": "\\n", "\r": "\\r", `"`: `\"`, "\x1a": "\\Z"}

    for old, want := range mysqlEscapeList {
        testEscape(t, old, want)
    }
    testEscape(t, `<p>123</p><div><img width="1080" />`, `<p>123</p><div><img width=\"1080\" />`)
}
func testEscape(t *testing.T, origin, want string) {
    escaped := MysqlRealEscapeString(origin)
    assert.Equal(t, want, escaped)
}
"
, "\n": "\\n", "\r": "\\r", `"`: `\"`, "\x1a": "\\Z"}
for old, want := range mysqlEscapeList { testEscape(t, old, want) } testEscape(t, `<p>123</p><div><img width="1080" />`, `<p>123</p><div><img width=\"1080\" />`) } func testEscape(t *testing.T, origin, want string) { escaped := MysqlRealEscapeString(origin) assert.Equal(t, want, escaped) }
  1. 使用这个替代方案
func Escape(sql string) string {
    dest := make([]byte, 0, 2*len(sql))
    var escape byte
    for i := 0; i < len(sql); i++ {
        c := sql[i]

        escape = 0

        switch c {
        case 0: /* Must be escaped for 'mysql' */
            escape = '0'
            break
        case '\n': /* Must be escaped for logs */
            escape = 'n'
            break
        case '\r':
            escape = 'r'
            break
        case '\\':
            escape = '\\'
            break
        case '\'':
            escape = '\''
            break
        case '"': /* Better safe than sorry */
            escape = '"'
            break
        case '2': //十进制26,八进制32,十六进制1a, /* This gives problems on Win32 */
            escape = 'Z'
        }

        if escape != 0 {
            dest = append(dest, '\\', escape)
        } else {
            dest = append(dest, c)
        }
    }

    return string(dest)
}
英文:

I came up with my own solution to create the function myself.
Hope it would be useful to someone.

func MysqlRealEscapeString(value string) string {
    replace := map[string]string{&quot;\\&quot;:&quot;\\\\&quot;, &quot;&#39;&quot;:`\&#39;`, &quot;\
func MysqlRealEscapeString(value string) string {
replace := map[string]string{&quot;\\&quot;:&quot;\\\\&quot;, &quot;&#39;&quot;:`\&#39;`, &quot;\\0&quot;:&quot;\\\\0&quot;, &quot;\n&quot;:&quot;\\n&quot;, &quot;\r&quot;:&quot;\\r&quot;, `&quot;`:`\&quot;`, &quot;\x1a&quot;:&quot;\\Z&quot;}
for b, a := range replace {
value = strings.Replace(value, b, a, -1)
}
return value;
}
&quot;:&quot;\\\
func MysqlRealEscapeString(value string) string {
replace := map[string]string{&quot;\\&quot;:&quot;\\\\&quot;, &quot;&#39;&quot;:`\&#39;`, &quot;\\0&quot;:&quot;\\\\0&quot;, &quot;\n&quot;:&quot;\\n&quot;, &quot;\r&quot;:&quot;\\r&quot;, `&quot;`:`\&quot;`, &quot;\x1a&quot;:&quot;\\Z&quot;}
for b, a := range replace {
value = strings.Replace(value, b, a, -1)
}
return value;
}
&quot;, &quot;\n&quot;:&quot;\\n&quot;, &quot;\r&quot;:&quot;\\r&quot;, `&quot;`:`\&quot;`, &quot;\x1a&quot;:&quot;\\Z&quot;} for b, a := range replace { value = strings.Replace(value, b, a, -1) } return value; }

1.MysqlRealEscapeString is not right, as below test case will fail

func TestEscape(t *testing.T) {
	mysqlEscapeList := map[string]string{
		&quot;\\&quot;: &quot;\\\\&quot;, &quot;&#39;&quot;: `\&#39;`, &quot;\
func TestEscape(t *testing.T) {
mysqlEscapeList := map[string]string{
&quot;\\&quot;: &quot;\\\\&quot;, &quot;&#39;&quot;: `\&#39;`, &quot;\\0&quot;: &quot;\\\\0&quot;, &quot;\n&quot;: &quot;\\n&quot;, &quot;\r&quot;: &quot;\\r&quot;, `&quot;`: `\&quot;`, &quot;\x1a&quot;: &quot;\\Z&quot;}
for old, want := range mysqlEscapeList {
testEscape(t, old, want)
}
testEscape(t, `&lt;p&gt;123&lt;/p&gt;&lt;div&gt;&lt;img width=&quot;1080&quot; /&gt;`, `&lt;p&gt;123&lt;/p&gt;&lt;div&gt;&lt;img width=\&quot;1080\&quot; /&gt;`)
}
func testEscape(t *testing.T, origin, want string) {
escaped := MysqlRealEscapeString(origin)
assert.Equal(t, want, escaped)
}
&quot;: &quot;\\\
func TestEscape(t *testing.T) {
mysqlEscapeList := map[string]string{
&quot;\\&quot;: &quot;\\\\&quot;, &quot;&#39;&quot;: `\&#39;`, &quot;\\0&quot;: &quot;\\\\0&quot;, &quot;\n&quot;: &quot;\\n&quot;, &quot;\r&quot;: &quot;\\r&quot;, `&quot;`: `\&quot;`, &quot;\x1a&quot;: &quot;\\Z&quot;}
for old, want := range mysqlEscapeList {
testEscape(t, old, want)
}
testEscape(t, `&lt;p&gt;123&lt;/p&gt;&lt;div&gt;&lt;img width=&quot;1080&quot; /&gt;`, `&lt;p&gt;123&lt;/p&gt;&lt;div&gt;&lt;img width=\&quot;1080\&quot; /&gt;`)
}
func testEscape(t *testing.T, origin, want string) {
escaped := MysqlRealEscapeString(origin)
assert.Equal(t, want, escaped)
}
&quot;, &quot;\n&quot;: &quot;\\n&quot;, &quot;\r&quot;: &quot;\\r&quot;, `&quot;`: `\&quot;`, &quot;\x1a&quot;: &quot;\\Z&quot;} for old, want := range mysqlEscapeList { testEscape(t, old, want) } testEscape(t, `&lt;p&gt;123&lt;/p&gt;&lt;div&gt;&lt;img width=&quot;1080&quot; /&gt;`, `&lt;p&gt;123&lt;/p&gt;&lt;div&gt;&lt;img width=\&quot;1080\&quot; /&gt;`) } func testEscape(t *testing.T, origin, want string) { escaped := MysqlRealEscapeString(origin) assert.Equal(t, want, escaped) }
  1. use this one instead
func Escape(sql string) string {
	dest := make([]byte, 0, 2*len(sql))
	var escape byte
	for i := 0; i &lt; len(sql); i++ {
		c := sql[i]

		escape = 0

		switch c {
		case 0: /* Must be escaped for &#39;mysql&#39; */
			escape = &#39;0&#39;
			break
		case &#39;\n&#39;: /* Must be escaped for logs */
			escape = &#39;n&#39;
			break
		case &#39;\r&#39;:
			escape = &#39;r&#39;
			break
		case &#39;\\&#39;:
			escape = &#39;\\&#39;
			break
		case &#39;\&#39;&#39;:
			escape = &#39;\&#39;&#39;
			break
		case &#39;&quot;&#39;: /* Better safe than sorry */
			escape = &#39;&quot;&#39;
			break
		case &#39;2&#39;: //十进制26,八进制32,十六进制1a, /* This gives problems on Win32 */
			escape = &#39;Z&#39;
		}

		if escape != 0 {
			dest = append(dest, &#39;\\&#39;, escape)
		} else {
			dest = append(dest, c)
		}
	}

	return string(dest)
}

答案2

得分: 4

如果整个查询 - 或者查询的任何部分超出了单个值 - 是从命令行传递的,那么你就没有需要转义的内容。

mysql_real_escape_string及其类似函数用于对单个值进行清理,以防止在将其插入查询之前,任何可以访问该值的人“突破”并篡改查询本身。

考虑到你将整个查询提供给外部,转义函数无法提高安全性。

在这里,你唯一能做的安全措施是:

  • 在无法造成任何损害的用户上下文中执行查询(例如,你可以在MySQL中按用户基础限制命令)
  • 确保正确捕获和处理查询错误
  • 如上面的评论中Not_a_Golfer所建议的,解析查询以查找任何恶意内容
英文:

If the entire query - or any part of the query that goes beyond a single value - is passed from the command line, there is nothing for you to escape.

mysql_real_escape_string and its cousins are for sanitizing single values, to prevent anyone with access to the value before it is inserted into the query from "breaking out" and fiddling with the query itself.

Given that you are giving access to the entire query to an outside, there is nothing an escape function could do to improve safety.

Your only shot at security here is

  • executing the query in a user context that can't do any damage (e.g. you can restrict commands on a per-user basis in mySQL)
  • making sure that query errors are properly caught and dealt with
  • as Not_a_Golfer suggests in the comments above, parsing the query for anything malicious

答案3

得分: 2

改进的答案:

func MysqlRealEscapeString(value string) string {
	var sb strings.Builder
	for i := 0; i < len(value); i++ {
		c := value[i]
		switch c {
		case '\\', 0, '\n', '\r', '\'', '"':
			sb.WriteByte('\\')
			sb.WriteByte(c)
		case '2':
			sb.WriteByte('\\')
			sb.WriteByte('Z')
		default:
			sb.WriteByte(c)
		}
	}
	return sb.String()
}
英文:

Improved answer:

func MysqlRealEscapeString(value string) string {
	var sb strings.Builder
	for i := 0; i &lt; len(value); i++ {
		c := value[i]
		switch c {
		case &#39;\\&#39;, 0, &#39;\n&#39;, &#39;\r&#39;, &#39;\&#39;&#39;, &#39;&quot;&#39;:
			sb.WriteByte(&#39;\\&#39;)
			sb.WriteByte(c)
		case &#39;2&#39;:
			sb.WriteByte(&#39;\\&#39;)
			sb.WriteByte(&#39;Z&#39;)
		default:
			sb.WriteByte(c)
		}
	}
	return sb.String()
}

答案4

得分: 0

你可以使用预处理查询来实现这个功能。

检索多行数据

stmt, err := db.Prepare("select id, name from users where id = ?")
if err != nil {
	log.Fatal(err)
}
defer stmt.Close()
rows, err := stmt.Query(1)
if err != nil {
	log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
	// ...
}
if err = rows.Err(); err != nil {
	log.Fatal(err)
}

检索单行数据

var name string
err = db.QueryRow("select name from users where id = ?", 1).Scan(&name)
if err != nil {
	log.Fatal(err)
}
fmt.Println(name)
英文:

For this you can use Prepared Queries.

To Retrieve Multiple Rows

stmt, err := db.Prepare(&quot;select id, name from users where id = ?&quot;)
if err != nil {
	log.Fatal(err)
}
defer stmt.Close()
rows, err := stmt.Query(1)
if err != nil {
	log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
	// ...
}
if err = rows.Err(); err != nil {
	log.Fatal(err)
}

To Retrieve Single Row

var name string
err = db.QueryRow(&quot;select name from users where id = ?&quot;, 1).Scan(&amp;name)
if err != nil {
	log.Fatal(err)
}
fmt.Println(name)

huangapple
  • 本文由 发表于 2015年7月27日 15:37:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/31647406.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定