Go web中cookie值安全securecookie库使用原理

目录
  • 引言
  • securecookie小档案
  • 一、安装
  • 二、使用示例
    • 明文的cookie值输出
    • 使用securecookie包对cookie值进行编码
    • 使用securecookie对value加密
    • 对cookie值进行解码
  • 三、实现原理
    • 序列化
    • 加密
    • base64编码
    • 使用hmac做hash
  • 四、beego框架中的cookie安全
  • 五、总结

引言

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

securecookie小档案

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

一、安装

 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包对cookie值进行编码

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”。

这里需要注意的是,解码的时候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编码的具体应用和实现原理大家可参考我的另外一篇文章:

使用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进行传输,以降低泄露的风险。

以上就是Go web中cookie值安全securecookie库使用原理的详细内容,更多关于Go web securecookie库的资料请关注我们其它相关文章!

(0)

相关推荐

  • Go语言开发浏览器视频流rtsp转webrtc播放

    目录 1. 前言 2. rtsp转webRTC 3. 初步测试结果 4. 结合我们之前的onvif+gSoap+cgo的方案做修改 4.1 go后端修改 4.2 前端修改 4.3 项目结构和编译运行 4.4 结果展示 5. 最后 1. 前言 前面我们测试了rtsp转hls方式,发现延迟比较大,不太适合我们的使用需求.接下来我们试一下webrtc的方式看下使用情况. 综合考虑下来,我们最好能找到一个go作为后端,前端兼容性较好的前后端方案来处理webrtc,这样我们就可以结合我们之前的cgo+on

  • Go语言对前端领域的入侵WebAssembly运行原理

    目录 引言 WebAssembly 运行原理 Go WebAssembly 运行原理 Go WebAssembly 初体验 第一步 第二步 第三步 第四步 第五步 Javascript 真的需要担心 Go WebAssembly 的威胁么? 引言 从 Go 语言诞生以来,它就开始不断侵蚀 Java .C.C++ 语言的领地.今年下半年 Go 语言发布了 1.11 版本,引入了 WebAssembly 技术,浏览器端 Javascript 的垄断地位也开始遭遇 Go 语言的攻击.这次不同以往,它意

  • golang基于websocket通信tcp keepalive研究记录

    目录 为什么有tcp Keepalive? tcp Keepalive是否默认开启? 如何设置tcp keepalive? 在Linux内核设置 golang websocket 客户端默认怎么处理tcp keepalive? golang websocket 服务器默认怎么处理tcp keepalive? 为什么有tcp Keepalive? 服务器和客户端建立tcp连接以后,客户端/服务器如何知道对方是否挂掉了? 这时候TCP协议提出一个办法,当客户端端等待超过一定时间后自动给服务端发送一个

  • Go Web编程添加服务器错误和访问日志

    目录 前言 初始化日志记录器 添加错误日志 添加访问日志 前言 错误日志和访问日志是一个服务器必须支持的功能,我们教程里使用的服务器到目前为止还没有这两个功能.正好前两天也写了篇介绍logrus日志库的文章,那么今天的文章里就给我们自己写的服务器加上错误日志和访问日志的功能.在介绍添加访问日志的时候会介绍一种通过编写中间件获取HTTP响应的StausCode和Body的方法. Go Web 编程系列的每篇文章的源代码都打了对应版本的软件包,供大家参考.公众号中回复gohttp11获取本文源代码

  • go语言beego框架web开发语法笔记示例

    目录 两个跳转语法 模型创建 获取post请求传过来的值 获取字符串 获取文件 获取文件后缀 orm查询表所有数据 前端循环语法 前端格式化时间 前端url传值方式 两个跳转语法 第一个参数是请求路径,第二个参数是http状态码. c.Redirect("/login",400) //重定向 c.TplName = "login.html" 模型创建 设置主键 `pk`设置自增 `auto` 注意:当Field类型为int,int32,int64,uint,uint

  • Go web入门Go pongo2模板引擎

    目录 下载 pongo2 函数库 从字符串中读取模板 从文件中读取文本 Go pongo2 迭代 Go pongo2 过滤 Go pongo2 条件 总结 Go pongo2 教程展示了如何使用 pongo2 模板引擎在 Golang 中使用模板. 模板引擎是一个库,旨在将模板与数据结合起来以生成文档.模板引擎用于生成大量电子邮件.源代码预处理或生成动态 HTML 页面. 模板由静态数据和动态区域组成.动态区域稍后被数据替换.渲染函数稍后将模板与数据结合起来.模板引擎用于将模板与数据模型相结合以

  • Golang Web 框架Iris安装部署

    目录 引言 Iris 安装 快速上手 在Iris中使用RESTful风格的API 静态站点部署 引言 Iris作为Golang中新兴的Web框架,比Gin晚了两年,于2016年开源,到目前为止已具备了较为完备的功能,参考Iris官方的数据可以发现,Iris不论在响应速度上还是吞吐量上都比同类型的Web框架高,通过本文可以了解如何在Web项目中使用Iris框架. 官方文档:www.iris-go.com/docs/#/?id=… Iris 安装 和其他Web框架一样,通过 go module 来安

  • Go WEB框架使用拦截器验证用户登录状态实现

    目录 wego拦截器 main函数 登录逻辑 登录拦截器的实现 index页面的实现 wego拦截器 wego拦截器是一个action(处理器函数)之前或之后被调用的函数,通常用于处理一些公共逻辑.拦截器能够用于以下常见问题: 请求日志记录 错误处理 身份验证处理 wego中有以下拦截器: before_exec :执行action之前拦截器 after_exec :执行action之后拦截器 本文用一个例子来说明如何使用拦截器来实现用户登录状态的判定.在这个例子中,用户访问login_get来

  • Go web中cookie值安全securecookie库使用原理

    目录 引言 securecookie小档案 一.安装 二.使用示例 明文的cookie值输出 使用securecookie包对cookie值进行编码 使用securecookie对value加密 对cookie值进行解码 三.实现原理 序列化 加密 base64编码 使用hmac做hash 四.beego框架中的cookie安全 五.总结 引言 今天给大家推荐的是web应用安全防护方面的另一个包:securecookie.该包给cookie中存储的敏感信息进行编.解码及解密.解密功能,以保证数据

  • java web中使用cookie记住用户的账号和密码

    毕业设计中需要用到记住账号密码的功能,网上搜到了一个解决方案,自己稍加改造就是下面的方法. 首先是登录的页面,当用户勾选记住密码,传递给controller(我用的SSM框架),后台设置cookie的值,然后下次登录的时候就不用再次输入账号和密码了. login.jsp的代码: <%@page import="org.apache.commons.lang.StringUtils"%> <%@ page language="java" conten

  • Go gorilla securecookie库的安装使用详解

    目录 简介 快速使用 使用 JSON 自定义编解码 Hash/Block 函数 更换 Key 总结 简介 cookie 是用于在 Web 客户端(一般是浏览器)和服务器之间传输少量数据的一种机制.由服务器生成,发送到客户端保存,客户端后续的每次请求都会将 cookie 带上.cookie 现在已经被多多少少地滥用了.很多公司使用 cookie 来收集用户信息.投放广告等. cookie 有两大缺点: 每次请求都需要传输,故不能用来存放大量数据: 安全性较低,通过浏览器工具,很容易看到由网站服务器

  • 解析PHP的Yii框架中cookie和session功能的相关操作

    Sessions 和 请求 和 响应类似, 默认可通过为yii\web\Session 实例的session 应用组件 来访问sessions. 开启和关闭 Sessions 可使用以下代码来开启和关闭session. $session = Yii::$app->session; // 检查session是否开启 if ($session->isActive) ... // 开启session $session->open(); // 关闭session $session->clo

  • ASP.NET中Cookie状态的说明与用法

    Cookie 最早出现是在Netscape Navigator 2.0 中.后来 ASP 也引入了这个技术,它的作用是与 Session 对象相结合来识别用户.每当用户开始连接站点时,系统将自动在内存块中创建一个用户有关的会话状态,同时创建一个用户的 ID 存放在浏览器端,与当前的用户惟一地联系起来.这样,服务器保存了 Session,浏览器保存了 Cookie(用户的 ID).当下一次用户发出请求时,请求的用户将被要求提交用户的 ID,两者对照以正确地还原原来的会话状态.这就是在无状态协议的

  • CI框架中cookie的操作方法分析

    本文实例讲述了CI框架中cookie的操作方法.分享给大家供大家参考.具体分析如下: 第一种设置cookie的方式:采用php原生态的方法设置的cookie的值 复制代码 代码如下: setcookie("user_id",$user_info['user_id'],86500);  setcookie("username",$user_info['username'],86500);  setcookie("password",$user_in

  • 详解PHP中cookie和session的区别及cookie和session用法小结

    具体来说 cookie 是保存在"客户端"的,而session是保存在"服务端"的 cookie 是通过扩展http协议实现的 cookie 主要包括 :名字,值,过期时间,路径和域: 如果cookie不设置生命周期,则以浏览器关闭而关闭,这种cookie一般存储在内存而不是硬盘上.若设置了生命周期则相反,不随浏览器的关闭而消失,这些cookie仍然有效直到超过设定的过 期 时间. session 一种类似散列表的形式保存信息, 当程序需要为某个客户端的请求创建一个

  • JS中cookie的使用及缺点讲解

     什么是Cookie Cookie意为"甜饼",是由W3C组织提出,最早由Netscape社区发展的一种机制.目前Cookie已经成为标准,所有的主流浏览器如IE.Netscape.Firefox.Opera等都支持Cookie. 由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份.怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证.这样服务器就能从通行证上确认客户身份了.这就是Cookie的工作原理. Cookie实际上是一小段的文本信

  • Java中Cookie和Session的那些事儿

    Cookie和Session都是为了保持用户的访问状态,一方面为了方便业务实现,另一方面为了简化服务端的程序设计,提高访问性能.Cookie是客户端(也就是浏览器端)的技术,设置了Cookie之后,每次访问服务端,请求中都会带上Cookie:Session是服务端技术,在服务端存储用户的访问信息. 使用Cookie传递信息,随着Cookie个数增多和访问量增大,它占用的带宽会越来越大:使用Session保存信息,最大的弱点就是不容易在多台服务器之间共享. 1 Cookie 通俗地讲,当用户使用H

  • 全面了解servlet中cookie的使用方法

    ---恢复内容开始--- Cookie是存储在客户端计算机上的文本文件,并保留了它们的各种信息跟踪的目的. Java Servlet透明支持HTTP Cookie. 涉及标识返回用户有三个步骤: • 服务器脚本发送到浏览器的一组cookie.对于如: 姓名,年龄,或识别号码等. • 浏览器将这些信息存储在本地计算机上,以备将来使用. • 下一次浏览器发送任何请求,Web服务器,然后这些cookie发送信息到服务器,服务器将使用这些信息来识别用户. 以下是有用的方法列表时,可以使用servlet操

随机推荐