【问题标题】:Double encoding when retrieving data saved to blob in MYSQL检索保存到 MYSQL 中 blob 的数据时的双重编码
【发布时间】:2022-01-13 03:16:10
【问题描述】:

我有一个包含数据的嵌套 python 列表,例如显示的长列表。 我有将这个列表变成类似字符串的 json 的代码,然后我对其进行编码以便将其提供给 base64.b64encoder,然后将其作为 blob 与其他一些数据一起保存到我的 mysql DB 中。当我尝试检索数据并将其转换回列表时,我遇到了我只能描述为双重编码的问题。

longlist = [
    [1, 2,'thisisdhdh'],
    [1,2,'thlsldfsdf'],
    [2,0,'sdlfjldksjflksdj']
] 

string1 = json.dumps(longlist)

encod1 = string1.encode('ascii')

encodedlist = base64.b64encode(encod1)

cur.execute('INSERT INTO conversations (convoblob) VALUES ("%s")' % (encodedlist))

code above to save the encoded list to the DB

code below to retrieve the data

cur.execute('SELECT convoblob FROM conversations WHERE id IN (327) ')
item = cur.fetchone()

x = items[0] 

1.输出类型类字节
b"b'W1sxLCAyLCAidGhpc2lzZGhkaCJdLCBbMSwgMiwgInRobHNsZGZzZGYiXSwgWzIsIDAsICJzZGxmamxka3N1.qZmxrc2RqIl1d'"

       x = items[0].decode('ascii')

1.输出类型类'str' 1.b'W1sxLCAyLCAidGhpc2lzZGhkaCJdLCBbMSwgMiwgInRobHNsZGZzZGYiXSwgWzIsIDAsICJzZGxmamxka3NqZ1.mxrc2RqIl1d'

正如你所看到的,一旦解码它就会变成一个我无法运行解码器的字符串。

不将数据保存到数据库会很好地解码,由于某种原因,一旦数据进入数据库,它会以 (b") 开头的 sting 和 (") 结尾

我的数据库默认字符集是 utf8mb4

我确实找到了一种解决方法,即解码从数据库中提取的数据,并将字符串末尾的位截断,将其标识为类似字符串的字节,然后将字符串编码回来,然后对其进行解码使用 base64.b64decode

cur.execute('SELECT convoblob FROM conversations WHERE id IN (327) ')
item = cur.fetchone()
decoded = item[0].decode('ascii')
y = len(decoded) - 1
newString = decoded[2:y]
this = newString.encode('ascii')
man = base64.b64decode(this).decode('ascii')
ohyeah = json.loads(man)
print(ohyeah[1])

输出为 b'W1sxLCAyLCAidGhpc2lzZGhkaCJdLCBbMSwgMiwgInRobHNsZGZzZGYiXSwgWzIsIDAsICJzZGxmamxka3NqZmxrc2RqIl1d'

[1, 2, 'thlsldfsdf']

【问题讨论】:

  • 你为什么要经历这些繁琐的事情?如上所述将您的列表转换为 JSON 字符串,然后将其保存在数据库中。然后只需一步即可重新创建您的列表。
  • 因为数据是我想要保留的 python 嵌套列表。有人建议在 base 64 中对其进行编码,以免从 sql 注入问题,我不知道那是什么,但听起来很可怕。
  • 防御SQL注入的方法不是应用一些ad hoc编码,而是使用prepared statements。请注意 Grismar 的回答中的 conn.execute('INSERT INTO conversations VALUES (?)', [encodedlist]) 行:这是一个准备好的语句,不受 SQL 注入攻击的影响。在那里使用您的 JSON 编码列表,您可以在检索 JSON 时对其进行解码。
  • 摆脱base64(来来去去);将“%s”更改为绑定。
  • 谢谢。老实说,我不知道注射是什么意思。我虽然这是数据库的注入,但出于性能原因,它会为数据添加一些符号。所以你说注入是指恶意行为者注入一些会破坏我的数据库的代码。好的,这没什么好担心的,因为这不是一个对互联网开放的数据库。使用我现在拥有的数据库,我已经在数据库中有大约 1k 条记录。并且将拍摄以建立大约 50k 或更多的记录。继续编码是否有内存优势?

标签: python mysql encoding blob


【解决方案1】:

这是怎么回事:

import sqlite3
import json
import base64

longlist = [
    [1, 2,'thisisdhdh'],
    [1,2,'thlsldfsdf'],
    [2,0,'sdlfjldksjflksdj']
] 

# you create a string
# '[[1, 2, "thisisdhdh"], [1, 2, "thlsldfsdf"], [2, 0, "sdlfjldksjflksdj"]]'
string1 = json.dumps(longlist)

# you create a bytes object from the string, happens to be encoded as ascii
# b'[[1, 2, "thisisdhdh"], [1, 2, "thlsldfsdf"], [2, 0, "sdlfjldksjflksdj"]]'
encod1 = string1.encode('ascii')

# you create a base-64 encoding of that object, still bytes
# b'W1sxLCAyLCAidGhpc2lzZGhkaCJdLCBbMSwgMiwgInRobHNsZGZzZGYiXSwgWzIsIDAsICJzZGxmamxka3NqZmxrc2RqIl1d'
encodedlist = base64.b64encode(encod1)

conn = sqlite3.connect('test.db')
conn.execute('DROP TABLE IF EXISTS conversations')
conn.execute('CREATE TABLE conversations (convoblob BLOB)')
# you format that bytes object into a string (casting it to a string) <<< your problem
conn.execute('INSERT INTO conversations VALUES ("%s")' % (encodedlist))

cur = conn.execute('SELECT convoblob FROM conversations')
item = cur.fetchone()

# no surprise about the mangled string here, since it was cast back into a string first
# "b'W1sxLCAyLCAidGhpc2lzZGhkaCJdLCBbMSwgMiwgInRobHNsZGZzZGYiXSwgWzIsIDAsICJzZGxmamxka3NqZmxrc2RqIl1d'"
print(item[0])

# Let's try again
conn.close()
conn = sqlite3.connect('test.db')
conn.execute('DROP TABLE IF EXISTS conversations')
conn.execute('CREATE TABLE conversations (convoblob BLOB)')
# now passing the bytes directly to the database, as bytes, into a blob
conn.execute('INSERT INTO conversations VALUES (?)', [encodedlist])

cur = conn.execute('SELECT convoblob FROM conversations')
item = cur.fetchone()

# This works:
my_list = json.loads(base64.b64decode(item[0]))
print(my_list)

我知道你使用 MySQL,但它对 SQLite 的工作方式相同(并且有同样的问题),所以我确定这是你的问题,sqlite3 适用于任何标准的 Python 安装。

因此,简而言之:通过使用 %-formatting 将 bytes 对象格式化为字符串,您创建的 "b'data'" 对象会给您带来麻烦。这可以通过将bytes 本身传递给 SQL 引擎并使用查询参数将其传递给查询来避免,无论如何这是避免 SQL 注入的更好方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-12
    • 2023-04-08
    • 1970-01-01
    • 2015-08-04
    • 1970-01-01
    • 2015-06-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多