英文:
Is mgo bson marshalling guaranteed to preserve order of struct components?
问题
我正在使用mgo将Go结构体保存在Mongo中。我希望使用结构体的哈希值(和一个密钥)来确定它们是否被篡改(并且我不希望Mongo数据库本身知道密钥)。
目前,我正在使用gob
将结构体序列化为字节流,然后对其进行哈希。gob
对结构体成员的顺序有明确定义。这种方法很好用,但当我从Mongo中重新读取结构体时,发现有些东西发生了变化——准确说,Mongo中的时间值精度比Go中的时间值低,因此哈希值不匹配。
我计划的解决方法是在计算哈希之前,先将结构体从BSON进行编组和解组,即:
- 将结构体编组为BSON
- 从BSON解组结构体(从而丢失时间的精度)
- 将结构体编组为字节流并计算哈希值
- 将哈希值放入结构体
- 将结构体保存到Mongo
这个方法有点绕,如果我能确保BSON本身始终保持结构体成员的顺序,我可以:
- 将结构体编组为BSON
- 计算哈希值
- 将哈希值放入结构体
- 将结构体保存到Mongo
这样会简单一些(尽管仍然需要两次转换为BSON)。
有什么想法吗?
英文:
I am saving go structs in mongo using mgo. I wish to save them with a hash of that struct (and a secret) to determine whether they have been tampered with (and I do not wish the mongo db itself to have the secret).
Currently I am hashing the structs by serializing them using gob
whose ordering of struct components is well-defined. This works great, save when I go to reread the struct from mango, things have changed - to be precise the time values in mongo have truncated accuracy compared to go - therefore the hashes do not match up.
My planned work around for this is simply to marshall and unmarshall the struct from BSON before calculating the hash, i.e.:
- Marshal struct to BSON
- Unmarshal struct from BSON (thereby losing precision on time)
- Marshall struct to gob and hash resultant
[]byte
- Put hash in struct
- Save struct to mongo
Now, that's more than a little circuitous.
If I could guaranteed that the BSON itself always preserved order of components in structs, I could:
- Marshal struct to BSON
- hash resultant
byte[]
- Put hash in struct
- Save struct to mongo
Which would be less nasty (albeit that it would still require converting to BSON twice).
Any ideas?
答案1
得分: 1
回答你的实际问题,是的,你可以相信mgo/bson
会按照代码中观察到的顺序始终编组结构字段。尽管这个行为尚未被记录下来(问题),但这是非常有意的行为,甚至mgo本身也依赖于它。
现在,回答你的预期用法:不要这样做。字段的顺序保证并不意味着整个二进制格式是稳定的。即使现在已知有方法可以改变输出而不破坏现有的客户端,但这将破坏其输出的哈希值。
以下是一些建议阅读,以更好地理解你试图解决的问题的细节(我是作者):
- http://blog.labix.org/2013/06/25/strepr-v1
- http://blog.labix.org/2013/07/03/reference-strepr-implementation
这正是解决以稳定方式编组任意结构或具有任意字段和键的映射的问题的问题,以便为签名获得稳定的哈希值。参考实现是用Go语言编写的。
英文:
Answering your actual question, yes, you can trust mgo/bson
to always marshal struct fields in the order they are observed in the code. Although not yet documented (issue), this is very much intentional behavior, and even mgo itself depends on it internally.
Now, responding to your intended usage: don't do that. The fact fields are in a guaranteed order does not mean the binary format is stable as a whole. Even now there are known ways the output can change without breaking even the existing clients, but that would break a hash of its output.
Here is some suggested reading to understand a bit better the ins and outs of the problem you are trying to solve (I'm the author):
- http://blog.labix.org/2013/06/25/strepr-v1
- http://blog.labix.org/2013/07/03/reference-strepr-implementation
This is precisely addressing the issue of marshaling an arbitrary or map with arbitrary fields and keys in a stable way, to obtain a stable hash out of it for signatures. The reference implementation is in Go.
答案2
得分: 0
BSON保持顺序。我会用几个简单的包装函数来处理所有的秘密:
type StructWrap struct{
Value bson.Raw
Hash []byte
}
func save2mgo(in interface{}){
str := StructWrap{}
orig,_err := bson.Marshal(in)
str.Value = bson.Raw{0, orig}
str.Hash = <根据orig字节进行哈希>
//将str保存到mgo。因为Value已经是bson.Raw,所以不会再次进行编组。
}
func unmarshal(inType interface{}){
//从mgo中读取
//在这里检查str.Hash
str.Value.Unmarshal(inType)
}
希望能对你有所帮助。
英文:
BSON keeps order. I would do all secret in a couple of simple wrap function:
type StructWrap struct{
Value bson.Raw
Hash []byte
}
func save2mgo(in interface{}){
str := StructWrap{}
orig,_err := bson.Marshal(in)
str.Value = bson.Raw{0, orig}
str.Hash = <do hash based on orig bytes>
//to save str to mgo. because Value already in bson.Raw, it will not do marshal again.
}
func unmarshal(inType interface{}){
//read from mgo
//check str.Hash here
str.Value.Unmarshal(inType)
}
hope can help you.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论