String 是否可以将Golang字符串';“记忆”;“安全”吗;?
最近,我在一个项目中使用cgo设置了String 是否可以将Golang字符串';“记忆”;“安全”吗;?,string,security,memory,go,cgo,String,Security,Memory,Go,Cgo,最近,我在一个项目中使用cgo设置了libnadium,以便使用crypto\u pwhash\u str和crypto\u pwhash\u str\u verify函数 这一切都进行得非常顺利,我现在有一个小函数集合,它以纯文本密码的形式接收一个[]字节,然后对它进行哈希运算,或者将它与另一个[]字节进行比较以验证它 我之所以使用[]字节而不是字符串,是因为,从我到目前为止对Go的了解来看,我至少可以循环使用纯文本密码并将所有字节归零,甚至可以将指针传递到libnaid的钠memzero函数
libnadium
,以便使用crypto\u pwhash\u str
和crypto\u pwhash\u str\u verify
函数
这一切都进行得非常顺利,我现在有一个小函数集合,它以纯文本密码的形式接收一个[]字节
,然后对它进行哈希运算,或者将它与另一个[]字节
进行比较以验证它
我之所以使用[]字节
而不是字符串
,是因为,从我到目前为止对Go的了解来看,我至少可以循环使用纯文本密码并将所有字节归零,甚至可以将指针传递到libnaid
的钠memzero
函数,为了不让它在内存中停留的时间超过它需要的时间
这对于我能够直接以字节形式读取输入的应用程序来说很好,但我现在正尝试在一个小型web应用程序中使用它,在这个应用程序中,我需要使用POST
方法从表单读取密码
从Go源代码和文档中可以看到,在请求处理程序中使用r.ParseForm
将所有表单值解析为string
s的map
问题是,因为Go中的string
s是不可变的,所以我认为我无法将表单中POST
ed的密码的内存归零;至少,只使用Go
因此,似乎我唯一(简单)的选择是将不安全的.Pointer
与字节数一起传递到C中的函数中,并让C为我将内存归零(例如,将其传递到前面提到的Na钠memzero
函数)
我已经尝试过这个方法,毫不奇怪它当然会起作用,但是我在Go中留下了一个不安全的字符串
,如果在fmt.Println
这样的函数中使用它,程序就会崩溃
我的问题如下:
- 我是否应该接受密码将被
ed并解析为字符串,我不应该弄乱它,而只是等待GC启动?(不理想)POST
- 如果代码中明确说明字符串变量不应再次使用,那么使用cgo将
字符串的内存归零是否可行
- 使用cgo将
字符串
的内存归零是否会导致GC崩溃
- 是否值得为
编写一种修饰符,它添加了一个函数,将表单值直接解析为http.Request
,这样我就可以完全控制它们到达时的值[]字节
编辑:为了澄清这一点,web应用程序和表单
POST
只是一个简单的例子,我可能仅仅通过使用Go的标准库以字符串的形式获得敏感数据。我更感兴趣的是我所有的问题是否可能/值得在某些情况下,尽快清理内存中的数据更是一个安全问题。鉴于在这个问题上似乎没有太多活动,我只想假设大多数人以前不需要/不想研究这个问题,或者觉得不值得花时间。因此,尽管我对Go的内部工作机制一无所知,但我还是将自己的发现作为答案发布
在回答这个问题之前,我应该先声明,因为Go是一种垃圾收集语言,我不知道它在内部是如何工作的,下面的信息可能根本不能保证任何内存被清除为零,但这不会阻止我尝试;毕竟,在我看来,内存中的纯文本密码越少越好
考虑到这一点,这就是我发现的与libnail
一起工作的一切(据我所知);到目前为止,至少我的任何程序都没有崩溃
首先,正如您可能已经知道的那样,Go中的string
s是不可变的,因此从技术上讲,它们的值不应该更改,但是如果我们使用不安全的指针,通过Cgo将指向Go中或C中的string
字符串,我们实际上可以覆盖存储在string
值中的数据;我们不能保证在内存中的任何地方都没有其他数据拷贝
出于这个原因,我让我的密码相关函数专门处理[]字节
变量,以减少内存中可能复制的纯文本密码的数量
我还返回传递到所有密码函数中的纯文本密码的[]byte
引用,因为将字符串
转换为[]byte
将分配新内存并复制内容。这样,至少如果您将字符串
就地转换为[]字节
,而不首先将其分配给变量,那么您仍然可以在函数调用完成后访问新的[]字节
,并将内存归零
下面是我想到的要点。您可以填空,包括libnail
C库,并编译它以自己查看结果
对我来说,它在调用MemZero*
函数之前输出:
pwd : Correct Horse Battery Staple
pwdBytes: [67 111 114 114 101 99 116 32 72 111 114 115 101 32 66 97 116 116 101 114 121 32 83 116 97 112 108 101]
pwd :
pwdBytes: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
Hash: $argon2i$v=19$m=131072,t=6,p=1$N05osI8nuTjftzfAYBIcbA$3yb92yt9S9dRmPtlSV/J8jY4DG3reqm+2eV+fi54Its
然后调用MemZero*
函数之后:
pwd : Correct Horse Battery Staple
pwdBytes: [67 111 114 114 101 99 116 32 72 111 114 115 101 32 66 97 116 116 101 114 121 32 83 116 97 112 108 101]
pwd :
pwdBytes: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
Hash: $argon2i$v=19$m=131072,t=6,p=1$N05osI8nuTjftzfAYBIcbA$3yb92yt9S9dRmPtlSV/J8jY4DG3reqm+2eV+fi54Its
因此,它看起来很成功,但由于我们不能保证内存中其他地方没有明文密码的副本,我认为这是我们所能做到的
下面的代码只是将一个带有字节数的不安全指针
传递到C中的Na钠memzero
函数来实现这一点。因此,内存的实际归零取决于libnail
如果我在代码中留下任何拼写错误或任何不起作用的东西,我表示歉意,但我不想粘贴太多,只想重新发布