【发布时间】:2017-02-19 10:53:56
【问题描述】:
最近我一直在使用 cgo 在我的一个项目中设置libsodium,以便使用crypto_pwhash_str 和crypto_pwhash_str_verify 函数。
这一切都进行得非常顺利,我现在有一小部分函数以纯文本密码的形式接收[]byte,然后对其进行哈希处理,或者将其与另一个[]byte 进行比较以验证它。
我使用[]byte 而不是string 的原因是,根据我目前所学到的关于 Go 的知识,我至少可以遍历纯文本密码并将所有字节归零,或者甚至将指针传递给libsodium 的sodium_memzero 函数,以免它在内存中停留的时间超过它需要的时间。
这对于我能够将输入直接读取为字节的应用程序来说很好,但我现在正尝试在一个小型 Web 应用程序中使用它,我需要使用 POST 方法从表单中读取密码。
从我在 Go 源代码和文档中可以看到,在请求处理程序中使用 r.ParseForm 会将所有表单值解析为 strings 的 map。
问题在于,因为 Go 中的 strings 是不可变的,所以我认为我无法将表单中 POSTed 的密码的内存归零;至少,只使用 Go。
所以看来我唯一(简单)的选择是将 unsafe.Pointer 连同字节数一起传递给 C 中的函数,然后让 C 为我将内存归零(例如,将其传递给前面提到的sodium_memzero函数)。
我已经尝试过了,不出所料,它当然可以工作,但是我在 Go 中留下了一个不安全的 string,如果在像 fmt.Println 这样的函数中使用它会导致程序崩溃。
我的问题如下:
- 我是否应该接受密码将是
POSTed 并被解析为字符串,并且我不应该弄乱它并等待 GC 启动? (不理想) - 是否可以使用 cgo 将
string的内存归零,前提是代码中明显记录了不应再次使用字符串变量? - 使用 cgo 将
string的内存归零会导致 GC 崩溃吗? - 是否值得为
http.Request编写一种装饰器,添加一个函数以将表单值直接解析为[]byte,这样我就可以在它们到达时完全控制它们?
编辑:澄清一下,网络应用程序和表单POST 只是一个方便的例子,我可能只是通过使用 Go 的标准库以 @ 的形式传递敏感数据987654346@。我更感兴趣的是我的所有问题是否可能/是否值得在某些情况下尽快清理内存中的数据更多的是安全问题。
【问题讨论】:
-
当您说将内存归零时,您的意思是调用 realloc 并将其大小设为 0?因为如果您的意思是将块中的所有位设置为 0,我不确定重点是什么。我的直觉说你应该依赖 GC,我认为你可以安排你的代码,以便地图很快离开范围(比如将字符串传递给你在 goroutine 中调用的函数并移动一个),所以我看不到内存被认为确实是一个问题。我的意思是,这里的内存问题是什么?密码不是很重要。
-
如果密码是通过 HTTP POST 输入的,那么将特定字符串归零没有多大意义,因为您自己没有直接从网络上读取它,也不知道那里有多少副本一直在记忆中。
-
@JimB 这是非常正确的,也许我应该更好地表达我的问题,因为我似乎已经给出了我想在我的 Web 应用程序中执行此操作的印象。确实,Web 应用程序示例只是触发了我对这个主题的想法,我更感兴趣的是简单地将 Go
strings 内存归零是否真的可能/“安全”足以在 Go 中执行,如果安全性这么高是必需的。 -
@JakeLucas:不,没有“安全”的方法可以做到这一点,并且该语言不保证字符串的内部处理。
-
@JimB 这就是我认为的答案,但只是想在这里发帖以检查我的理解。我所说的“安全”是指,如果您小心不要再次使用字符串变量。当然,由于无法保证字符串的内部处理,所以不管你多么小心,最好不要管它,但有时我忍不住想要更直接地使用内存:)跨度>
标签: string security memory go cgo