【问题标题】:Object Transmission in Python using Pickle使用 Pickle 在 Python 中传输对象
【发布时间】:2018-02-25 13:37:23
【问题描述】:

我有以下类,一个 Point 对象

class Point:
    def __init__(self):
        pass

    def __init__(self, x, y):
        self.x = x
        self.y = y

我有一个服务器(使用 UDP)

# Server side
import socket
import pickle

host = "localhost"
port = 10000

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host, port))

data = s.recvfrom(1024)
print(data)

我的客户端是:

import socket
import pickle
from Point import *

host = "localhost"
port = 10000
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

p = Point(10, 20)
a = pickle.dumps(p)

s.sendto(a, (host, port))

在服务器端,每当我得到p 并打印它时,我都会得到以下(b'\x80\x03cPoint\nPoint\nq\x00)\x81q\x01}q\x02(X\x01\x00\x00\x00xq\x03K\nX\x01\x00\x00\x00yq\x04K\x14ub.', ('127.0.0.1', 55511))

我怎样才能得到对象,而不是这个?

【问题讨论】:

  • ...使用pickle.loads? “它不起作用”是什么意思?它会抛出错误吗?它会使您的PC崩溃吗?它是否返回一个圆而不是一个点?
  • @Aran-Fey 我这样做了,但我仍然无法加载创建对象,我更新了我的问题以显示我尝试过的内容
  • 为什么你有 两个 __init__ 方法?第一个定义是无效的,被第二个取代。
  • 强烈想提醒大家不要在客户端-服务器应用程序中使用 Pickle,除非你想敞开心扉让进程被劫持。 Pickles 允许任意代码执行。
  • @MartijnPieters 谢谢!我更新了问题。尽管如此,出于好奇,还是想知道如何实现这一目标。

标签: python sockets pickle


【解决方案1】:

首先,请注意。 Pickles 允许任意代码执行。不要不要使用它来接受任意连接,最好使用加密技术来确保您只交换受信任的数据。即便如此,请考虑使用更安全的交换格式。

接下来,考虑到 UDP 数据包的大小是有限的。您需要确保您的 pickle 数据足够小以适合 UDP 数据包(最大有效负载大小为 65507 字节)。至少当您收到数据包时,您会知道您拥有所有数据。使用 65535 作为缓冲区大小,以确保可以完全接收大数据包。

发送时,请确保您没有超出大小限制:

MAX_UDP_SIZE = 65507  # https://en.wikipedia.org/wiki/User_Datagram_Protocol

a = pickle.dumps(p)
if len(a) > MAX_UDP_SIZE:
    raise ValueError('Message too large')
s.sendto(a, (host, port))

另一方面,使用pickle.loads() 将pickle 数据流转回对象:

UDP_MAX = 2 ** 16 - 1

data, addr = s.recvfrom(UDP_MAX)
object = pickle.loads(data)

我强烈敦促您至少验证 addr 是否受信任,否则您可以任由自己执行任意代码。 65507 字节是足够的空间来发送一个控制你的进程的泡菜。

如果您需要发送更多数据,则需要使用 TCP 而不是 UDP,因为您必须以特定顺序跨数据发送、分布在多个数据包中,并且需要所有数据包到达接收端; TCP 提供了这一层可靠性。那时你必须在你的泡菜前面加上固定数量的字节来编码泡菜的大小,这样你就可以确保你在另一边再次读取相同数量的数据。

【讨论】:

  • 关于框架的cmets是错误的。 OP使用UDP。框架是数据报。要处理的正确数据量是已知的。
  • @Jean-PaulCalderone:现在专门针对 UDP,感谢您指出这一点。
  • 在公共互联网上获得 2 ** 16 字节 UDP 数据包的机会非常小。您可以通过 LAN 将其关闭。在 UDP 上伪造发件人地址非常容易,因此不要依赖报告的发件人地址进行访问控制。对于加密货币,很容易出错。您几乎可以肯定需要使用现有的、被广泛接受的解决方案。考虑带证书的 DTLS 或 也许 NaCL SecretBox。
  • (但确实有很多 Pickle 的替代品,只是不要使用它。)
  • Python 有很好的cryptography support 但你需要知道你在做什么,是的。
【解决方案2】:

Pickle 不适合此用途。考虑this warning

警告 pickle 模块不能防止错误或 恶意构造的数据。永远不要解开从 不受信任或未经身份验证的来源。

您的协议缺乏任何类型的访问控制,因此您的进程容易受到任意远程代码执行攻击。

请考虑使用真实的网络协议。有很多可供选择。例如,Cap' Proto

【讨论】:

  • 虽然如此,但这是一个单独的问题,而不是问题的答案。
  • 这里忽略安全后果是不负责任的。
【解决方案3】:

解决方案是在服务器端使用pickle.loads,如下所示

d = s.recvfrom(1024)
p1 = Point(1,1)
p1 = pickle.loads(d[0])
print(str(p1.x))

但是,正如许多人所建议的那样,pickle 可能不是此类情况的最佳解决方案。

【讨论】:

  • 如果您发送的数据大于该大小,则读取固定的 1024 字节会导致问题。 UDP 有您需要考虑的大小限制。
猜你喜欢
  • 2020-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-29
  • 2019-10-15
相关资源
最近更新 更多