「Go工具箱」web中想让cookie值更安全?推荐使用securecookie包

发表于 2年以前  | 总阅读数:523 次

在工作中,主动性不仅体现在像老黄牛一样把本职工作做好,还要主动和领导沟通,承担更多、更重要的任务。--- 吴军 《格局》

今天给大家推荐的是web应用安全防护方面的另一个包:securecookie。该包给cookie中存储的敏感信息进行编、解码及解密、解密功能,以保证数据的安全。

securecookie小档案

securecookie小档案
star 595 used by 12.1k
contributors 19 作者 Gorilla
功能简介 对cookie中存储的敏感信息进行编码、解码以及加密、解密功能,以保证数据不能被伪造。
项目地址 https://github.com/gorilla/securecookie
相关知识 web安全、加密解密、HMAC编码解码、base64编码

一、安装

使用go get进行安装

 go get github.com/gorilla/securecookie 

二、使用示例

明文的cookie值输出

我们先来看下未进行编码或未加密的cookie输出是什么样的。本文以beego框架为例,当然在beego中已经实现了安全的cookie输出,稍后再看其具体的实现。这里主要是来说明cookie中未编码的输出和使用securecookie包后cookie的值输出。

package main

import (
 "github.com/beego/beego"
)


func main() {
 beego.Router("/", &MainController{})

 beego.RunWithMiddleWares(":8080")
}

type MainController struct {
 beego.Controller
}

func (this *MainController) Get() {
 this.Ctx.Output.Cookie("userid", "1234567")
 this.Ctx.Output.Body([]byte("Hello World"))
}

执行go run main.go,然后在浏览器中输入http://localhost:8080/,查看cookie的输出是明文的。如下:

使用securecookie对value值编码

securecookie包的使用也很简单。首先使用securecookie.New函数实例化一个securecookie实例,在实例化的时候需要传入一个32位或64位的hashkey值。然后调用securecookie实例的Encode对明文值进行编码即可。如下示例:

package main

import (
 "github.com/beego/beego"
 "github.com/gorilla/securecookie"
)


func main() {
 beego.Router("/", &MainController{})

 beego.RunWithMiddleWares(":8080")
}

type MainController struct {
 beego.Controller
}

func (this *MainController) Get() {
 // Hash keys should be at least 32 bytes long
 var hashKey = []byte("keep-it-secret-keep-it-safe-----")
 // 实例化securecookie
 var s = securecookie.New(hashKey, nil)

 name := "userid"
 value := "1234567"

    // 对value进行编码
 encodeValue, _ := s.Encode(name, value)

    // 输出编码后的cookie值
 this.Ctx.Output.Cookie(name, encodeValue)
 this.Ctx.Output.Body([]byte("Hello World"))
}

以下是经过securecookie编码后的cookie值输出结果:

在调用securecookie.New时,第一个参数hashKey是必须的,推荐使用32字节或64字节长度的key。因为securecookie底层编码时是使用HMAC算法实现的,hmac算法在对数据进行散列操作时会进行加密。

securecookie包不仅支持对字符串的编码和加密。还支持对结构体及自定义类型进行编码和加密。下面示例是对一个map[string]string类型进行编/解码的实例。

package main

import (
 "fmt"
 "github.com/beego/beego"
 "github.com/gorilla/securecookie"
)


func main() {
 beego.Router("/", &MainController{})

 beego.RunWithMiddleWares(":8080")
}

type MainController struct {
 beego.Controller
}

func (this *MainController) Get() {
 // Hash keys should be at least 32 bytes long
 var hashKey = []byte("keep-it-secret-keep-it-safe-----")

 // Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
 // Shorter keys may weaken the encryption used.
 var blockKey = []byte("1234567890123456")
 // 实例化securecookie
 var s = securecookie.New(hashKey, blockKey)

 value := map[string]string{
  "id": "1234567",
 }

 name := "userid"
 //value := "1234567"
 //
 encodeValue, err := s.Encode(name, value)
 fmt.Println("encodeValue:", encodeValue, err)

    // 解析到decodeValue中
 decodeValue := make(map[string]string)
 s.Decode(name, encodeValue, &decodeValue)
 fmt.Println("decodeValue:", decodeValue)

 this.Ctx.Output.Cookie(name, encodeValue)
 this.Ctx.Output.Body([]byte("Hello World"))
}

当然,其他类型也是支持的。大家有兴趣的可以自行看下源码。

使用securecookie对value加密

securecookie不止可以对明文值进行编码,而且还可以对编码后的值进一步加密,使value值更安全。加密也很简单,就是在调用securecookie.New的时候传入第二个参数:加密秘钥即可。如下:


 // Hash keys should be at least 32 bytes long
 var hashKey = []byte("keep-it-secret-keep-it-safe-----")

 // Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
 // Shorter keys may weaken the encryption used.
 var blockKey = []byte("1234567890123456")
 // 实例化securecookie
 var s = securecookie.New(hashKey, blockKey)

 name := "userid"
 value := "1234567"

 encodeValue, err := s.Encode(name, value)

以下是经过securecookie加密后的cookie值输出结果:

在securecookie包中,是否对cookie值进行加密是可选的。在调用New时,如果第二个参数传nil,则cookie值只进行hash,而不加密。如果给第二个参数传了一个值,即秘钥,则该包还会对hash后的值再进行加密处理。这里需要注意,加密秘钥的长度必须是16字节或32字节,否则会加密失败。

对cookie值进行解码

有编码就有解码。在收到请求中的cookie值后,就可以使用相同的securecookie实例对cookie值进行解码了。如下:

package main

import (
 "fmt"
 "github.com/beego/beego"
 "github.com/gorilla/securecookie"
)


func main() {
 beego.Router("/", &MainController{})

 beego.RunWithMiddleWares(":8080")
}

type MainController struct {
 beego.Controller
}

func (this *MainController) Get() {
 // Hash keys should be at least 32 bytes long
 var hashKey = []byte("keep-it-secret-keep-it-safe-----")

 // Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
 // Shorter keys may weaken the encryption used.
 var blockKey = []byte("1234567890123456")
 // 实例化securecookie
 var s = securecookie.New(hashKey, blockKey)

 encodeValue := this.Ctx.GetCookie("userid")

 value := ""
 s.Decode("userid", encodeValue, &value)
 fmt.Println("decode value is :", value, encodeValue)

 this.Ctx.Output.Cookie("userid", value)

 this.Ctx.Output.Body([]byte("Hello World"))
}

该示例是我们把上次加密的cookie值发送给本次请求,服务端进行解码后写入到cookie中。本次输出正好是明文“1234567”。

image.png

这里需要注意的是,解码的时候Decode的第一个参数是cookie的name值。第二个参数才是cookie的value值。这是成对出现的。后面在讲编码的实现原理时会详细讲解。

三、实现原理

securecookie包的Encode函数的实现主要有两点:加密和hash转换。同样Decode的过程与Encode是相反的。

Encode函数的实现流程如下:

序列化

第一步为什么要把value值进行序列化呢?我们看securecookie.Encode接口,如下:

func (s *SecureCookie) Encode(name string, value interface{}) (string, error)

我们知道cookie中的值是key-value形式的。这里name就是cookie中的key,value是cookie中的值。我们注意到value的类型是interface{}接口,也就是说value可以是任意数据类型(结构体,map,slice等)。但cookie中的value只能是字符串。所以,Encode的第一步就是把value值进行序列化。

序列化有两种方式,分别是内建的包encoding/json和encoding/gob。securecookie包默认使用gob包进行序列化:

func (e GobEncoder) Serialize(src interface{}) ([]byte, error) {
 buf := new(bytes.Buffer)
 enc := gob.NewEncoder(buf)
 if err := enc.Encode(src); err != nil {
  return nil, cookieError{cause: err, typ: usageError}
 }
 return buf.Bytes(), nil
}

知识点:encoding/json和encoding/gob的区别:gob包比json包生成的序列化数据体积更小、性能更高。但gob序列化的数据只适用于go语言编写的程序之间传递(编码/解码)。而json包适用于任何语言程序之间的通信。

如果在编码过程中想使用json对value值进行序列化,那么可以通过SetSerialize方法进行设置,如下:

cookie := securecookie.New([]byte("keep-it-secret-keep-it-safe-----")
cookie.SetSerializer(securecookie.JSONEncoder{})

加密

加密是可选的。如果在调用secrecookie.New的时候指定了第2个参数,那么就会对序列化后的数据加密操作。如下:

 // 2. Encrypt (optional).
 if s.block != nil {
  if b, err = encrypt(s.block, b); err != nil {
   return "", cookieError{cause: err, typ: usageError}
  }
 }

加密使用的AES对称加密。在Go的内建包crypto/aes中。该包有5种加密模式,5种模式之间采用的分块算法不同。有兴趣的同学可以自行深入研究。而securecookie包采用的是CTR模式。如下是加密相关代码:

func encrypt(block cipher.Block, value []byte) ([]byte, error) {
 iv := GenerateRandomKey(block.BlockSize())
 if iv == nil {
  return nil, errGeneratingIV
 }
 // Encrypt it.
 stream := cipher.NewCTR(block, iv)
 stream.XORKeyStream(value, value)
 // Return iv + ciphertext.
 return append(iv, value...), nil
}

该对称加密算法其实还可以应用其他具有敏感信息的传输中,比如价格信息、密码等。

base64编码

经过上述编码(或加密)后的数据实际上是一串字节序列。如果转换成字符串大家可以看到会有乱码的出现。这里的乱码实际上是不可见字符。如果想让不可见字符变成可见字符,最常用的就是使用base64编码。

base64编码是将二进制字节转换成文本的一种编码方式。该编码方式是将二进制字节转换成可打印的asc码。就是先预定义一个可见字符的编码表,参考RFC4648文档。然后将原字符串的二进制字节序列以每6位为一组进行分组,然后再将每组转换成十进制对应的数字,再根据该数字从预定义的编码表中找到对应的字符,最终组成的字符串就是经过base64编码的字符串。

在base64编码中有4种模式:

  • base64.StdEncoding:标准模式是依据RFC 4648文档实现的,最终转换成的字符由A到Z、a-z、0-9以及+和 / 符号组成的。
  • base64.URLEncoding: URLEncoding模式最终转成的字符是由A到Z、a-z、0-9以及 - 和 _ 组成的。就是把标准模式中的+和/字符替换成了-和/。因为该模式主要应用于URL地址传输中,而在URL中+和/是保留字符,不能出现,所以讲其做了替换。
  • base64.RawEncoding: 该模式使用的字符集和StdEncoding一样。但该模式是按照位数来的,每6bits换为一个base64字符,就没有在尾部补齐到4的倍数字节了。
  • base64.RawURLEncoding: 该模式使用的字符集和URLEncoding模式一样。同样该模式也是按照位数来的,每6bits换为一个base64字符,就没有在尾部补齐到4的倍数字节了。

base64编码的具体应用和实现原理大家可参考我的另外一篇文章:Go实战 | url和base64编码原理及应用

使用hmac做hash

简单来讲就是对字符串做了加密的hash转换。在上文中我们提到,加密是可选的,hmac才是必需的。如果没有使用加密,那么经过上述序列化、base64编码后的字符串依然是明文的。所以无论有没有加密,都要做一次hash。这里使用的是内建包crypto/hmac。

做hmac操作时,不是只对value值进行hash,而是经过了字符串的拼接。实际上是对cookie名、日期、value值三部分进行拼接,并用 "|"隔开进行的:

代码如下:

 // 3. Create MAC for "name|date|value". Extra pipe to be used later.
 b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b))
 mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1])

 // Append mac, remove name.
 b = append(b, mac...)[len(name)+1:]

 // 4. Encode to base64.
 b = encode(b)

这里将name值拼接进字符串是因为在加码验证的时候可以对key-value对进行验证,说明该value是属于该name值的。将时间戳拼接进去,主要是为了对cookie的有效期做验证。在解密后,用当前时间和字符串中的时间做比较,就能知道该cookie值是否已经过期了。

最后,将经过hmac的hash值除去name值后再和b进行拼接。拼接完,为了在url中传输,所以再做一次base64的编码。

相关知识:HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一种基于Hash函数和密钥进行消息认证的方法。其能提供两方面的内容:① 消息完整性认证:能够证明消息内容在传送过程没有被修改。② 信源身份认证:因为通信双方共享了认证的密钥,接收方能够认证发送该数据的信源与所宣称的一致,即能够可靠地确认接收的消息与发送的一致。

四、beego框架中的cookie安全

笔者查看了常用的web框架echo、gin、beego,发现只有在beego框架中集成了安全的cookie设置。但也只实现了用hmac算法对value值和时间戳做加密hash。该实现在Controller的SetSecureCookie函数中,如下:

// SetSecureCookie puts value into cookie after encoded the value.
func (c *Controller) SetSecureCookie(Secret, name, value string, others ...interface{}) {
 c.Ctx.SetSecureCookie(Secret, name, value, others...)
}

// SetSecureCookie Set Secure cookie for response.
func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
 vs := base64.URLEncoding.EncodeToString([]byte(value))
 timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
 h := hmac.New(sha256.New, []byte(Secret))
 fmt.Fprintf(h, "%s%s", vs, timestamp)
 sig := fmt.Sprintf("%02x", h.Sum(nil))
 cookie := strings.Join([]string{vs, timestamp, sig}, "|")
 ctx.Output.Cookie(name, cookie, others...)
}

五、总结

经过securecookie编码过的cookie值是不会被伪造的,因为该值是经过hmac进行编码的。而且还可以对编码过的值再进行一次对称加密。如果是敏感信息的话,建议不要存储在cookie中。同时,敏感的信息也一定使用https进行传输,以降低泄露的风险。

本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/mbzHHlP1fLZxJ3JqT3myfA

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237231次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8065次阅读
 目录