【问题标题】:How do you use SHA256 to create a token of key,value pairs and a secret signature?您如何使用 SHA256 创建密钥、值对和秘密签名的令牌?
【发布时间】:2012-01-12 05:07:25
【问题描述】:

我想在这些隐藏字段的键值对的 sha 编码字符串的帮助下验证一些隐藏的输入字段(以确保它们在提交时不会更改)。我在网上看到了这个例子,但我不明白如何编码和 使用动态秘密值解码这些值。有人可以帮助我了解如何在 perl 中执行此操作吗?

还有哪种签名类型(MD5、SHA1、SHA256 等)在性能和安全性之间取得了很好的平衡?

更新

那么,编码后如何解码字符串?

【问题讨论】:

    标签: perl encryption sha


    【解决方案1】:

    你真正需要的不是一个普通的哈希函数,而是一个message authentication code,比如HMAC。既然你说你想使用 SHA-256,你可能会喜欢 HMAC_SHA256,它在 Perl 中通过 Digest::SHA 模块提供:

    use Digest::SHA qw(hmac_sha256_base64);
    
    my $mac = hmac_sha256_base64( $string, $key );
    

    这里,$key 是一个任意密钥,你应该保密,$string 包含你要签名的数据。要将其应用于更复杂的数据结构(例如键值对的哈希),您首先需要将其转换为字符串。有几种方法可以做到这一点;例如,您可以使用Storable:

    use Storable qw(freeze);
    
    sub pairs_to_string {
        local $Storable::canonical = 1;
        my %hash = @_;
        return freeze( \%hash );
    }
    

    您还可以进行 URL 编码,如 suggested by David Schwartz。重要的是,无论您使用哪种方法,当给定与输入相同的哈希值时,它应该始终返回完全相同的字符串。

    然后,在将数据发送给用户之前,为他们计算 MAC 并将其作为额外字段包含在数据中。当您收到返回的数据时,您删除 MAC 字段(并保存其值),重新计算剩余字段的 MAC 并将其与您收到的值进行比较。如果它们不匹配,则有人(或某物)篡改了数据。像这样:

    my $key = "secret";
    sub mac { hmac_sha256_base64( pairs_to_string(@_), $key ) }
    
    # before sending data to client:
    my %data = (foo => "something", bar => "whatever");
    $data{mac} = mac( %data );
    
    # after receiving %data back from client:
    my $mac = delete $data{mac};
    die "MAC mismatch" if $mac ne mac( %data );
    

    请注意,此技术不会自动阻止一些潜在的技巧,例如replay attacks:一旦您将数据和 MAC 发送给用户,他们就会学习与特定数据集对应的 MAC,并且可能会用从早期表单中保存的值替换以后表单中的字段。为了保护自己免受此类攻击,您应该在受 MAC 保护的数据中包含足够的识别信息,以确保您可以检测到任何可能有害的重放。理想情况下,您希望在每个表单中都包含一个唯一的 ID,并检查没有任何 ID 曾经提交过两次,但这可能并不总是可行的。如果做不到这一点,最好包含一个用户 ID(这样恶意用户就不能欺骗其他人提交他们的数据)和一个表单 ID(这样用户就不能将数据从一个表单复制到另一个表单) ) 可能还有时间戳和/或会话 ID(以便您可以拒绝旧数据)在表单中(以及在 MAC 计算中)。

    【讨论】:

    • 你不(不能)解码它。您所做的是重新计算它并检查它是否与您返回的相同。我添加了一些示例代码来展示如何做到这一点。
    【解决方案2】:

    我不知道您所说的“解包”是什么意思,但是您无法从哈希中获取原始字符串。

    让我们了解一下这个问题:您渲染了一些隐藏字段,并且您希望确保它们被提交时保持不变,对吗?以下是确保这一点的方法。

    假设你有两个变量:

    first: foo
    second: bar
    

    您可以将它们与密钥一起散列:

    secret_key = "ysEJbKTuJU6u"
    source_string = secret_key + "first" + "foo" + "second" + "bar"
    hash = MD5(source_string)
    
    # => "1adfda97d28af6535ef7e8fcb921d3f0"    
    

    现在你可以渲染你的标记了:

    <input type="hidden" name="first" value="foo" />
    <input type="hidden" name="second" value="bar" />
    <input type="hidden" name="hash" value="1adfda97d28af6535ef7e8fcb921d3f0">
    

    提交表单后,您会获得 firstsecond 字段的值,以类似的方式将它们连接到您的密钥并再次散列。

    如果哈希值相等,则您的值没有改变。

    注意:从不向客户端呈现密钥。并在散列之前对键/值对进行排序(以消除对顺序的依赖)。

    (免责声明:我不是加密人,所以你现在可以停止阅读)

    至于性能/安全性,即使 MD5 被发现有一个弱点,它仍然非常有用,恕我直言。 SHA1 有一个理论上的弱点,虽然还没有成功的攻击。 SHA-256 没有已知的弱点。

    【讨论】:

    • 是的,在您发表评论之前添加了这一点 :-)
    【解决方案3】:

    对于这个应用程序,任何加密算法都可以。您可以以任何您想要的方式打包这些值,只要它是可重复的。一种常见的方法是将字段打包成一个字符串,就像您将它们编码成一个 GET 请求的 URL(名称=值)一样。

    要计算哈希,请创建一个可以是任何您想要的文本密钥。它应该至少有 12 个字节长。计算与打包字段连接的秘密的哈希值并将其附加到末尾。

    所以,假设你选择了 MD5,JS90320ERHe2 的秘密,你有这些字段:

    first_name = Jack
    last_name = Smith
    other_field = 7=2
    

    首先,对其进行 URL 编码:

    first_name=Jack&last_name=Smith&other_field=7%3d=2
    

    然后计算 MD5 散列

    JS90320ERHe2first_name=Jack&last_name=Smith&other_field=7%3d=2
    

    这是6d0fa69703935efaa183be57f81d38ea。最终编码字段为:

    first_name=Jack&last_name=Smith&other_field=7%3d=2&hash=6d0fa69703935efaa183be57f81d38ea
    

    这就是您传递给用户的内容。为了验证它,从最后删除哈希,通过连接剩下的秘密来计算 MD5 哈希,如果哈希匹配,则该字段没有被篡改。

    没有人可以计算自己的有效 MD5,因为他们不知道在字符串前面加上前缀。

    请注意,攻击者可以重复使用任何旧的有效值集。他们只是无法从头开始创建自己的值集或修改现有的值集并使其测试有效。因此,请确保您在信息中包含一些内容,以便您可以验证它是否适合它的使用目的。

    【讨论】:

    • 我喜欢 URL 参数化
    • 你从末尾去掉&amp;hash=...。验证哈希,然后像处理 GET 请求一样对字符串的其余部分进行 URL 解码。
    猜你喜欢
    • 2014-05-25
    • 2013-01-23
    • 2019-11-01
    • 1970-01-01
    • 2023-01-20
    • 1970-01-01
    • 2022-07-21
    • 2014-05-12
    • 2020-12-16
    相关资源
    最近更新 更多