hash - HTTP规范:不进行数据传输的PUT,因为服务器知道数据的哈希值

标签 hash webdav put rfc deduplication

HTTP / WebDav规范是否允许此客户端-服务器对话框?


客户端:我想将数据放入/user1/foo.mkv中,该哈希值是:HASH
服务器:好的,PUT成功,您不需要发送数据,因为我已经知道带有此哈希和的数据。


注意:此PUT是初始上传。这不是更新。

如果可能的话,可以实现一种更快的文件同步方式。

用例:WebDAV服务器为每个用户托管一个目录。最喜欢的视频foo.mkv被多个用户上传。在此示例中,喜爱的视频已经存储在以下位置:/user2/myfoo.mkv。由于服务器已经知道内容,因此第二次及之后的上载不需要发送任何数据。这将减少很多网络负载。

前提条件:


客户端和服务器需要事先就哈希算法达成共识。
服务器需要存储已知文件的哈希值。


在自定义客户端和服务器中实施此操作非常容易。但这不是我想要的。

我的问题:是否存在允许此类对话的RFC或其他标准?

如果还没有标准,那么如何实现这个梦想呢?

安全考虑

通过上面的对话框,将能够访问已知哈希的内容。例如,一个邪恶的客户知道存在一个哈希值为1234567...的文件。他可以执行上述两个步骤,然后客户端可以使用GET下载数据。

扩展对话框的一种方法:


客户端:我想输入具有该哈希值的数据:哈希
服务器:好的,PUT将成功,但是要确保您具有数据,请向我发送字节N到M。我需要这样做,以确保您具有哈希和和数据。
客户端:数据的第N个字节(最多M个)为abcde...
服务器:好的,您的字节匹配我的。我信任你。上传成功,您无需再发送数据。


如何做到这一点?

由于似乎还没有规范,因此问题的这一部分仍然存在:

如何实现这个梦想?

最佳答案

根据您的描述,似乎应该使用ETag

它是专门设计用于将标签(通常是MD5哈希,但可以是任何东西)与资源的内容(和/或位置)相关联,以便您以后可以知道资源是否已更改。

EUT支持PUT请求,并且commonly used具有optimistic concurrency controlIf-Match标头。

但是,您的用例略有不同,因为您尝试阻止对内容相同的资源进行PUT,而If-Match标头用于仅允许对内容相同的资源进行PUT。

在您的情况下,可以改用If-None-Match标头:


“ If-None-Match:*”的含义是该方法不得为
如果原始服务器(或
缓存,可能使用Vary机制,请参阅第14.44节),
如果该表示不存在,则应该执行。这个
该功能旨在防止PUT之间的竞争
操作。




WebDAV also supports Etags尽管其用法可能取决于实现:


请注意,在PUT响应中ETag的含义不清楚
在本文档或RFC 2616中定义(即ETag是否
表示该资源的八位字节等于八位字节的主体
PUT请求,或者服务器是否可以进行较小的更改
在存储时文档的格式或内容中)。这是一
HTTP问题,而不仅仅是WebDAV问题。




如果您要实现自己的客户,我将执行以下操作:


客户端向资源发送HEAD请求,检查ETag

如果客户端发现它匹配已经存在的内容,请不要发送其他任何内容
如果不匹配,则发送带有If-None-Matches标头的PUT请求





更新

从更新的问题来看,现在似乎很清楚,当收到PUT请求时,您想要在接受请求之前检查服务器上的所有资源是否缺少相同的内容。这还意味着还要检查与指定为PUT请求的目的地的位置不同的资源。

AFAIK,目前没有专门处理这种情况的规范。但是,ETag机制(和HTTP协议)被设计为通用且足够灵活以处理许多情况,这就是其中一种。

当然,这仅意味着您无法利用标准的HTTP服务器逻辑-您需要自定义客户端和服务器端的代码。

假设条件

在进行可能的实现之前,需要进行一些假设。


如前所述,您需要同时控制服务器和客户端
需要同意一种基于内容生成ETag的算法。可以是MD5,SHA1,SHA2-256,SHA3,它们的组合的串联等等。我仅将算法输出作为ETag提及,但是如何实现取决于您自己。


可能的实施

如果简单的情况对您不起作用,则将这些命令从最简单到逐渐增加的复杂程度排序。

可能的实施方式1

假定您的服务器实现允许您读取请求标头并在接收到整个请求之前进行响应。


客户端计算要上传的文件/资源​​的ETag。
客户端使用包含ETag的标头If-None-Match向服务器发送PUT请求(位置无关紧要),然后继续正常发送正文。
服务器检查以查看是否已经存在带有ETag的资源。
服务器:


如果ETag已经存在,请立即返回412响应代码。 (可选)终止连接以阻止客户端继续发送资源(注意:尽管没有明确禁止,但HTTP规范不建议这样做。请参阅下面的注释1)。是的,浪费了一点带宽,但是您不必等待整个请求完成。
如果ETag不存在,请等待请求正常完成。

客户:


如果收到412响应,请对其进行解释,以使资源已经存在并且需要中止请求-停止发送数据。



可能的实施方式2

这稍微复杂一点,但是更好地遵循了HTTP规范。另外,如果在接收到整个请求之前您的服务器体系结构doesn't allow you to read the headers,则此MIGHT可以工作。


客户端计算要上传的文件/资源​​的ETag。
客户端使用包含ETag的标头If-None-MatchExpect: 100-continue标头向服务器发送PUT请求(位置无关紧要)。此时尚未发送请求正文。
服务器检查以查看是否已经存在带有ETag的资源。
服务器:


如果ETag已经存在,则返回412 response
如果ETag不存在,请发送100 response并等待请求正常完成。

客户:


如果收到412响应,请对其进行解释,以使资源已经存在,因此请求中止。
如果收到100响应,请继续正常发送正文



可能的实施方式3

此实现可能需要最多的工作,但应与所有主要的库/体系结构广泛兼容。但是,另一个客户端在两个请求之间上传具有相同内容的文件的风险很小。


客户端计算要上传的文件/资源​​的ETag。
客户端将HEAD请求(无正文)发送到/check-etag/<etag>处的服务器,其中<etag>是ETag。这将检查服务器上是否已经存在ETag。
/check-etag/*处的服务器代码检查是否存在带有该ETag的资源。
服务器:


如果ETag已经存在,则返回200响应。
如果ETag不存在,请发送404响应。

客户:


如果收到200响应,请对其进行解释,以使资源已经存在并且不继续进行PUT请求。
如果收到404响应,请向目标目的地发送正常的PUT请求。



注意事项

尽管具体实现取决于您,但是需要考虑以下几点:


添加或更新资源时,应将ETag和位置存储在数据库中以便快速检索。每当资源上载时,服务器为每个单个资源重新计算哈希值都是不必要的低效率。 ETag和位置字段上还应该有一个索引,以便快速检索。
如果两个客户端同时上传具有相同ETag的资源,则您可能希望在第一个客户端完成后立即中止第二个客户端。
对ETag使用散列意味着存在冲突的可能性(其中两个资源将具有相同的散列),尽管在实践中,如果使用良好的散列,则可能性极小。注意,已知MD5对故意collision attacks较弱。如果您偏执狂,可以将多个散列连接起来,使碰撞的机率小得多。
关于您的“安全考虑”,我仍然看不到知道哈希如何导致资源检索。服务器只会并且应该仅告诉您是否存在特定的ETag。如果不泄露位置,客户端将无法检索文件。即使客户端知道位置,服务器也应该实施其他安全控制,例如身份验证和授权来限制访问。仅将资源位置用作限制访问的方法只是出于安全考虑(尤其是因为您提到的那样,路径似乎遵循用户名的模式)。


笔记


RFC 2616指示不应这样做:



如果原始服务器收到不包含Expect的请求
期望值为“ 100-continue”的请求标头字段
包括一个请求主体,服务器以最终状态响应
从传输读取整个请求正文之前的代码
连接,则服务器不应关闭传输连接
直到读取了整个请求,或者直到客户端关闭
连接。否则,客户端可能无法可靠地收到
响应消息。


另外,请勿在没有发送任何状态代码的情况下关闭服务器端的连接,因为客户端很可能会重试该请求:


如果HTTP / 1.1客户端发送包含请求正文的请求,
但其中不包含带有
预期为“ 100-继续”,并且客户不是直接
连接到HTTP / 1.1原始服务器,并且如果客户端看到
在从服务器接收任何状态之前,连接已关闭,
客户应该重试该请求。

关于hash - HTTP规范:不进行数据传输的PUT,因为服务器知道数据的哈希值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32794863/

相关文章:

Symfony2 <a> 链接 'post' 或 'delete' 或 'put' 方法

hash - 在实践中忽略 SHA 冲突的可能性是否安全?

java - 在java中使用散列来匹配模式

javascript - IT Hit cookies 通行证问题

java - slf4j 登录控制台而不是文件

c# - 使用 PUT 通过 C# HttpWebRequest 在 PHP 中检索数据 --- C# -> PHP 使用 Slim Rest Framework

ruby - 在 Ruby 中按哈希分组

Perl - 为什么按其键循环散列然后打印每个值会导致未初始化的警告?

mysql - CardDav 服务器和 MySQL 数据库之间的接口(interface)

php - 使用 PHP 和 cURL PUT 文件(内容)