在Python中计算并将哈希值添加到文件。

4
一种检查文件是否被修改的方法是计算并存储文件的哈希值(或校验和)。然后,在任何时候都可以重新计算哈希值并与存储的值进行比较。
我想知道是否有一种方法可以将文件的哈希值存储在文件本身中?我想到的是文本文件。
计算哈希值的算法应该是迭代的,并考虑到哈希值将被添加到正在计算哈希值的文件中...有什么可用的吗?
谢谢!

edit: https://security.stackexchange.com/questions/3851/can-a-file-contain-its-md5sum-inside-it


1
如果您将哈希存储在文件中,文件的哈希值将会改变。 - Klaus D.
如果你将校验和与文件一起传输,任何人现在都可以修改了,这样没有意义。 - Sergius
1
这与HMAC有何不同?您使用私钥对消息进行哈希处理。然后,当您稍后查看该消息时,尝试再次进行哈希处理,如果哈希值不匹配,则知道有人更改了某些内容。这是完整性问题,而非机密性问题。意思是:我认为这是一个非常合理的问题。 - dwanderson
@KlausD. 逻辑很严谨,但NTFS有更多的魔法... - Serge Ballesta
这里有一个快速的例子,尽管它由于我的"哈希函数"即异或的性质增加了一点复杂性。 如果你将文件中的所有字节xor在一起形成一个值,那么将最后一个字节存储为value ^ 0xff。 现在,当您读回文件以检查更改时,您可以检查所有字节(包括最后一个“幻数”字节)的xor是否等于0xff。 如果不是,则肯定有变化(如果是,那么变化可能很幸运-但这就是您仅使用8位检查的结果)。 此外,您可以将0xff更改为任何所需的值(实际上0x00更有意义...) - dwanderson
显示剩余3条评论
3个回答

2
from Crypto.Hash import HMAC
secret_key = "Don't tell anyone"
h = HMAC.new(secret_key)
text = "whatever you want in the file"
## or: text = open("your_file_without_hash_yet").read()
h.update(text)
with open("file_with_hash") as fh:
    fh.write(text)
    fh.write(h.hexdigest())

现在,有些人试图指出,尽管他们似乎很困惑 - 你需要记住这个文件的结尾有哈希值,并且哈希值本身不是被哈希的内容的一部分。因此,当您想要检查文件时,您可以采取以下步骤:

end_len = len(h.hex_digest())
all_text = open("file_with_hash").read()
text, expected_hmac = all_text[:end_len], all_text[end_len:]
h = HMAC.new(secret_key)
h.update(text)
if h.hexdigest() != expected_hmac:
    raise "Somebody messed with your file!"

请注意,仅仅使用hash算法并不能保证您的文件未被篡改;通常情况下需要对文件进行加密,并取明文的hash值。这样,如果有人更改了hash值(位于文件末尾),或者试图更改消息中的任何字符(加密部分),则会导致不匹配,从而知道文件已经被更改。
恶意用户无法更改文件并修复hash值以使其匹配,因为他们需要更改一些数据,然后用您的私钥重新计算所有内容的hash值。只要没有人知道您的私钥,他们就不知道如何重新创建正确的hash值。

1
这是一个有趣的问题。如果您采用适当的哈希约定并验证文件的完整性,就可以完成它。假设您有这个文件,即main.py:
#!/usr/bin/env python
# encoding: utf-8
print "hello world"

现在,您可以将SHA-1哈希作为注释附加到Python文件中:
(printf '#'; cat main.py | sha1sum) >> main.py

更新了 main.py 文件:

#!/usr/bin/env python
# encoding: utf-8
print "hello world"
#30e3b19d4815ff5b5eca3a754d438dceab9e8814  -

因此,要验证文件是否已修改,您可以在Bash中执行以下操作:
if [ "$(printf '#';head -n-1 main.py | sha1sum)" == "$(tail -n1 main.py)" ]
then
    echo "Unmodified"
else 
    echo "Modified"
fi

当然,有人可能会试图通过手动更改哈希字符串来愚弄你。为了阻止这些坏人,您可以在将哈希添加到最后一行之前,使用一个秘密字符串来篡改文件,从而改进系统。

改进版

在包括您的秘密字符串的最后一行中添加哈希:

(printf '#';cat main.py;echo 'MyUltraSecretTemperString12345') | sha1sum >> main.py

用于检查文件是否已修改:

if [ "$(printf '#';(head -n-1 main.py; echo 'MyUltraSecretTemperString12345') | sha1sum)" == "$(tail -n1 main.py)" ]
then
    echo "Unmodified"
else 
    echo "Modified"
fi

使用这个改进版本,只有当坏人先找到你的超级秘密钥匙时,他们才能欺骗你。
编辑:这是 keyed-hash message authentication code (HMAC)的一个粗糙实现。

你的建议和仅使用HMAC有区别吗? - dwanderson
@dwanderson,事实上我不知道HMAC方法。看起来我的答案以一种shell的方式处理了相同的方法。但是知道有一种更复杂的方法来处理这个任务是很好的。 - Arton Dorneles
很印象深刻,你能在脑海中几分钟内想出行业标准!干得好! - dwanderson
不幸的是,这并不是我脑海中的顶部 =(。我只记得JSON web tokens是如何签名和再现一般思路的。 - Arton Dorneles
啊,明白了。是的,这是签署文件的完全正常方式,我不确定为什么那么多原始评论者会困惑为什么有人想要这样做。 - dwanderson

0

虽然这听起来像一个奇怪的想法,但它可能是Windows NTFS文件系统中一个很少使用但非常强大的属性的应用:文件流

它允许在不改变默认流内容的情况下向文件添加多个。例如:

echo foo > foo.text
echo bar > foo.text:alt
type foo.text
=>   foo
more < foo.text:alt
=>   bar

但是当列出目录时,你只能看到一个单独的文件:foo.txt

因此,在你的使用情况中,你可以将主流的哈希值写入名为hash的流中,并在稍后将hash流的内容与主流的哈希值进行比较。

只是一个备注:由于我不知道什么原因,type foo.text:alt会生成以下错误:

"The filename, directory name, or volume label syntax is incorrect."

这就是为什么我的示例使用了MSDN上的使用流页面中推荐的more <

所以假设你有一个myhash函数,它为文件提供哈希值(你可以通过使用hashlib模块轻松构建一个):

def myhash(filename):
    # compute the hash of the file
    ...
    return hash_string

你可以做以下事情:

def store_hash(filename):
    hash_string = myhash(filename)
    with open(filename + ":hash") as fd:
        fd.write(hash_string)

def compare_hash(filename):
        hash_string = myhash(filename)
        with open(filename + ":hash") as fd:
            orig = fd.read()
        return (hash_string == orig)

这与帮助 OP 在 Python 中实现有什么关系? - dwanderson
@dwanderson:你说得对。我先回答了一些通用信息。现在我已经更新了我的帖子,加入了一些Python代码。 - Serge Ballesta
OP 特别要求将哈希存储在文件本身中,尽管问题的评论中有一些反对意见;您的解决方案没有考虑到这一点,并且您也没有帮助提供哈希函数,这似乎是原始问题的一个重要部分。 OP 想知道“我可以在文件后面存储哈希值”(您没有回答),“哈希应该是迭代的”意味着“我应该使用什么哈希函数或如何使用?”(您假设他们已经有哈希函数)。 - dwanderson
@dwanderson: 我已经解释过,备用数据流仍然包含在文件中。当您使用copy src dest复制文件时,您将复制所有的数据流。而且 OP 并没有要求将哈希值存储在文件之后,而是要求在文件内部存储哈希值。标准模块 hashlib 提供了足够的哈希函数。 - Serge Ballesta
关于流的观点很中肯 - 我被Windows的东西搞糊涂了,所以我也没跟上。我想如果是我回答的话,我会提到hashlib,因为问问题的人似乎不知道这个,并且正在寻求建议。 - dwanderson
@dwanderson:这就是为什么我最初的回答只涉及NTFS流的原因;-) - 无论如何,现在有了你的评论,情况变得更好了... - Serge Ballesta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接