亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Go?web中cookie值安全securecookie庫(kù)使用原理

 更新時(shí)間:2022年11月10日 16:45:35   作者:Go學(xué)堂  
這篇文章主要為大家介紹了Go?web中cookie值安全securecookie庫(kù)使用及實(shí)現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

今天給大家推薦的是web應(yīng)用安全防護(hù)方面的另一個(gè)包:securecookie。該包給cookie中存儲(chǔ)的敏感信息進(jìn)行編、解碼及解密、解密功能,以保證數(shù)據(jù)的安全。

securecookie小檔案

securecookie小檔案   
star595used by-
contributors19作者Gorilla
功能簡(jiǎn)介對(duì)cookie中存儲(chǔ)的敏感信息進(jìn)行編碼、解碼以及加密、解密功能,以保證數(shù)據(jù)不能被偽造。  
項(xiàng)目地址github.com/gorilla/sec…  
相關(guān)知識(shí)web安全、加密解密、HMAC編碼解碼、base64編碼  

一、安裝

 go get github.com/gorilla/securecookie 

二、使用示例

明文的cookie值輸出

我們先來看下未進(jìn)行編碼或未加密的cookie輸出是什么樣的。本文以beego框架為例,當(dāng)然在beego中已經(jīng)實(shí)現(xiàn)了安全的cookie輸出,稍后再看其具體的實(shí)現(xiàn)。這里主要是來說明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"))
}

執(zhí)行g(shù)o run main.go,然后在瀏覽器中輸入http://localhost:8080/,查看cookie的輸出是明文的。如下:

使用securecookie包對(duì)cookie值進(jìn)行編碼

securecookie包的使用也很簡(jiǎn)單。首先使用securecookie.New函數(shù)實(shí)例化一個(gè)securecookie實(shí)例,在實(shí)例化的時(shí)候需要傳入一個(gè)32位或64位的hashkey值。然后調(diào)用securecookie實(shí)例的Encode對(duì)明文值進(jìn)行編碼即可。如下示例:

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-----")
	// 實(shí)例化securecookie
	var s = securecookie.New(hashKey, nil)
	name := "userid"
	value := "1234567"
    // 對(duì)value進(jìn)行編碼
	encodeValue, _ := s.Encode(name, value)
    // 輸出編碼后的cookie值
	this.Ctx.Output.Cookie(name, encodeValue)
	this.Ctx.Output.Body([]byte("Hello World"))
}

以下是經(jīng)過securecookie編碼后的cookie值輸出結(jié)果:

在調(diào)用securecookie.New時(shí),第一個(gè)參數(shù)hashKey是必須的,推薦使用32字節(jié)或64字節(jié)長(zhǎng)度的key。因?yàn)閟ecurecookie底層編碼時(shí)是使用HMAC算法實(shí)現(xiàn)的,hmac算法在對(duì)數(shù)據(jù)進(jìn)行散列操作時(shí)會(huì)進(jìn)行加密。

securecookie包不僅支持對(duì)字符串的編碼和加密。還支持對(duì)結(jié)構(gòu)體及自定義類型進(jìn)行編碼和加密。下面示例是對(duì)一個(gè)map[string]string類型進(jìn)行編/解碼的實(shí)例。

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")
	// 實(shí)例化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"))
}

當(dāng)然,其他類型也是支持的。大家有興趣的可以自行看下源碼。

使用securecookie對(duì)value加密

securecookie不止可以對(duì)明文值進(jìn)行編碼,而且還可以對(duì)編碼后的值進(jìn)一步加密,使value值更安全。加密也很簡(jiǎn)單,就是在調(diào)用securecookie.New的時(shí)候傳入第二個(gè)參數(shù):加密秘鑰即可。如下:

	// 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")
	// 實(shí)例化securecookie
	var s = securecookie.New(hashKey, blockKey)
	name := "userid"
	value := "1234567"
	encodeValue, err := s.Encode(name, value)

以下是經(jīng)過securecookie加密后的cookie值輸出結(jié)果:

在securecookie包中,是否對(duì)cookie值進(jìn)行加密是可選的。在調(diào)用New時(shí),如果第二個(gè)參數(shù)傳nil,則cookie值只進(jìn)行hash,而不加密。如果給第二個(gè)參數(shù)傳了一個(gè)值,即秘鑰,則該包還會(huì)對(duì)hash后的值再進(jìn)行加密處理。這里需要注意,加密秘鑰的長(zhǎng)度必須是16字節(jié)或32字節(jié),否則會(huì)加密失敗。

對(duì)cookie值進(jìn)行解碼

有編碼就有解碼。在收到請(qǐng)求中的cookie值后,就可以使用相同的securecookie實(shí)例對(duì)cookie值進(jìn)行解碼了。如下:

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")
	// 實(shí)例化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值發(fā)送給本次請(qǐng)求,服務(wù)端進(jìn)行解碼后寫入到cookie中。本次輸出正好是明文“1234567”。

這里需要注意的是,解碼的時(shí)候Decode的第一個(gè)參數(shù)是cookie的name值。第二個(gè)參數(shù)才是cookie的value值。這是成對(duì)出現(xiàn)的。后面在講編碼的實(shí)現(xiàn)原理時(shí)會(huì)詳細(xì)講解。

三、實(shí)現(xiàn)原理

securecookie包Encode函數(shù)的實(shí)現(xiàn)主要有兩點(diǎn):加密和hash轉(zhuǎn)換。同樣Decode的過程與Encode是相反的。

Encode函數(shù)的實(shí)現(xiàn)流程如下:

序列化

第一步為什么要把value值進(jìn)行序列化呢?我們看securecookie.Encode接口,如下:

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

我們知道cookie中的值是key-value形式的。這里name就是cookie中的key,value是cookie中的值。我們注意到value的類型是interface{}接口,也就是說value可以是任意數(shù)據(jù)類型(結(jié)構(gòu)體,map,slice等)。但cookie中的value只能是字符串。所以,Encode的第一步就是把value值進(jìn)行序列化。

序列化有兩種方式,分別是內(nèi)建的包encoding/json和encoding/gob。securecookie包默認(rèn)使用gob包進(jìn)行序列化:

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
}

 知識(shí)點(diǎn):encoding/json和encoding/gob的區(qū)別:gob包比json包生成的序列化數(shù)據(jù)體積更小、性能更高。但gob序列化的數(shù)據(jù)只適用于go語(yǔ)言編寫的程序之間傳遞(編碼/解碼)。而json包適用于任何語(yǔ)言程序之間的通信。

如果在編碼過程中想使用json對(duì)value值進(jìn)行序列化,那么可以通過SetSerialize方法進(jìn)行設(shè)置,如下:

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

加密

加密是可選的。如果在調(diào)用secrecookie.New的時(shí)候指定了第2個(gè)參數(shù),那么就會(huì)對(duì)序列化后的數(shù)據(jù)加密操作。如下:

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

加密使用的AES對(duì)稱加密。在Go的內(nèi)建包c(diǎn)rypto/aes中。該包有5種加密模式,5種模式之間采用的分塊算法不同。有興趣的同學(xué)可以自行深入研究。而securecookie包采用的是CTR模式。如下是加密相關(guān)代碼:

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
}

該對(duì)稱加密算法其實(shí)還可以應(yīng)用其他具有敏感信息的傳輸中,比如價(jià)格信息、密碼等。

base64編碼

經(jīng)過上述編碼(或加密)后的數(shù)據(jù)實(shí)際上是一串字節(jié)序列。如果轉(zhuǎn)換成字符串大家可以看到會(huì)有亂碼的出現(xiàn)。這里的亂碼實(shí)際上是不可見字符。如果想讓不可見字符變成可見字符,最常用的就是使用base64編碼。 base64編碼是將二進(jìn)制字節(jié)轉(zhuǎn)換成文本的一種編碼方式。該編碼方式是將二進(jìn)制字節(jié)轉(zhuǎn)換成可打印的asc碼。就是先預(yù)定義一個(gè)可見字符的編碼表,參考RFC4648文檔。然后將原字符串的二進(jìn)制字節(jié)序列以每6位為一組進(jìn)行分組,然后再將每組轉(zhuǎn)換成十進(jìn)制對(duì)應(yīng)的數(shù)字,在根據(jù)該數(shù)字從預(yù)定義的編碼表中找到對(duì)應(yīng)的字符,最終組成的字符串就是經(jīng)過base64編碼的字符串。在base64編碼中有4種模式:

  • base64.StdEncoding:標(biāo)準(zhǔn)模式是依據(jù)RFC 4648文檔實(shí)現(xiàn)的,最終轉(zhuǎn)換成的字符由A到Z、a-z、0-9以及+和 / 符號(hào)組成的。
  • base64.URLEncoding: URLEncoding模式最終轉(zhuǎn)成的字符是由A到Z、a-z、0-9以及 - 和 _ 組成的。就是把標(biāo)準(zhǔn)模式中的+和/字符替換成了-和/。因?yàn)樵撃J街饕獞?yīng)用于URL地址傳輸中,而在URL中+和/是保留字符,不能出現(xiàn),所以講其做了替換。
  • base64.RawEncoding: 該模式使用的字符集和StdEncoding一樣。但該模式是按照位數(shù)來的,每6bits換為一個(gè)base64字符,就沒有在尾部補(bǔ)齊到4的倍數(shù)字節(jié)了。
  • base64.RawURLEncoding: 該模式使用的字符集和URLEncoding模式一樣。同樣該模式也是按照位數(shù)來的,每6bits換為一個(gè)base64字符,就沒有在尾部補(bǔ)齊到4的倍數(shù)字節(jié)了。

base64編碼的具體應(yīng)用和實(shí)現(xiàn)原理大家可參考我的另外一篇文章:

使用hmac做hash

簡(jiǎn)單來講就是對(duì)字符串做了加密的hash轉(zhuǎn)換。在上文中我們提到,加密是可選的,hmac才是必需的。如果沒有使用加密,那么經(jīng)過上述序列化、base64編碼后的字符串依然是明文的。所以無論有沒有加密,都要做一次hash。這里使用的是內(nèi)建包c(diǎn)rypto/hmac。

做hmac操作時(shí),不是只對(duì)value值進(jìn)行hash,而是經(jīng)過了字符串的拼接。實(shí)際上是對(duì)cookie名、日期、value值三部分進(jìn)行拼接,并用 "|"隔開進(jìn)行的:

代碼如下:

	// 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值拼接進(jìn)字符串是因?yàn)樵诩哟a驗(yàn)證的時(shí)候可以對(duì)key-value對(duì)進(jìn)行驗(yàn)證,說明該value是屬于該name值的。 將時(shí)間戳拼接進(jìn)去,主要是為了對(duì)cookie的有效期做驗(yàn)證。在解密后,用當(dāng)前時(shí)間和字符串中的時(shí)間做比較,就能知道該cookie值是否已經(jīng)過期了。

最后,將經(jīng)過hmac的hash值除去name值后再和b進(jìn)行拼接。拼接完,為了在url中傳輸,所以再做一次base64的編碼。

相關(guān)知識(shí):HMAC是密鑰相關(guān)的哈希運(yùn)算消息認(rèn)證碼(Hash-based Message Authentication Code)的縮寫,由H.Krawezyk,M.Bellare,R.Canetti于1996年提出的一種基于Hash函數(shù)和密鑰進(jìn)行消息認(rèn)證的方法。其能提供兩方面的內(nèi)容: ① 消息完整性認(rèn)證:能夠證明消息內(nèi)容在傳送過程沒有被修改。 ② 信源身份認(rèn)證:因?yàn)橥ㄐ烹p方共享了認(rèn)證的密鑰,接收方能夠認(rèn)證發(fā)送該數(shù)據(jù)的信源與所宣稱的一致,即能夠可靠地確認(rèn)接收的消息與發(fā)送的一致。

四、beego框架中的cookie安全

筆者查看了常用的web框架echo、gin、beego,發(fā)現(xiàn)只有在beego框架中集成了安全的cookie設(shè)置。但也只實(shí)現(xiàn)了用hmac算法對(duì)value值和時(shí)間戳做加密hash。該實(shí)現(xiàn)在Controller的SetSecureCookie函數(shù)中,如下:

// 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...)
}

五、總結(jié)

經(jīng)過securecookie編碼過的cookie值是不會(huì)被偽造的,因?yàn)樵撝凳墙?jīng)過hmac進(jìn)行編碼的。而且還可以對(duì)編碼過的值再進(jìn)行一次對(duì)稱加密。如果是敏感信息的話,建議不要存儲(chǔ)在cookie中。同時(shí),敏感的信息也一定使用https進(jìn)行傳輸,以降低泄露的風(fēng)險(xiǎn)。

以上就是Go web中cookie值安全securecookie庫(kù)使用原理的詳細(xì)內(nèi)容,更多關(guān)于Go web securecookie庫(kù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Golang中字符串的使用

    詳解Golang中字符串的使用

    這篇文章主要為大家詳細(xì)介紹了Golang中字符串的使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Golang有一定的幫助,感興趣的小伙伴可以了解一下
    2022-10-10
  • Go channel如何批量讀取數(shù)據(jù)

    Go channel如何批量讀取數(shù)據(jù)

    本文將展示一個(gè)從 Go channel 中批量讀取數(shù)據(jù),并批量發(fā)送到 Kafka 和批量寫入網(wǎng)絡(luò)數(shù)據(jù)的示例,文中的示例代碼講解詳細(xì),有需要的可以參考下
    2024-10-10
  • Golang實(shí)現(xiàn)Redis過期時(shí)間實(shí)例探究

    Golang實(shí)現(xiàn)Redis過期時(shí)間實(shí)例探究

    這篇文章主要介紹了Golang實(shí)現(xiàn)Redis過期時(shí)間實(shí)例探究,
    2024-01-01
  • 一文帶你深入了解Golang中的Mutex

    一文帶你深入了解Golang中的Mutex

    這篇文章主要為大家詳細(xì)介紹了Golang中Mutex的相關(guān)知識(shí),知其然,更要知其所以然。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2023-03-03
  • 詳解Golang的GC三色標(biāo)記法

    詳解Golang的GC三色標(biāo)記法

    這篇文章主要為大家介紹了Golang的GC三色標(biāo)記法,文中有詳細(xì)的實(shí)現(xiàn)過程供大家參考,對(duì)大家的學(xué)習(xí)或工作有一定幫助,感興趣的可以跟著小編一來看看
    2023-05-05
  • 淺析Go語(yǔ)言bitset的實(shí)現(xiàn)原理

    淺析Go語(yǔ)言bitset的實(shí)現(xiàn)原理

    bitset包是一個(gè)將非負(fù)整數(shù)映射到布爾值的位的集合,這篇文章主要通過開源包bitset來為大家分析一下位集合的設(shè)計(jì)和實(shí)現(xiàn),感興趣的可以學(xué)習(xí)一下
    2023-08-08
  • 如何使用Go語(yǔ)言實(shí)現(xiàn)基于泛型的Jaccard相似度算法

    如何使用Go語(yǔ)言實(shí)現(xiàn)基于泛型的Jaccard相似度算法

    這篇文章主要介紹了如何使用Go語(yǔ)言實(shí)現(xiàn)基于泛型的Jaccard相似度算法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-08-08
  • Go gRPC超時(shí)控制Deadlines用法詳解

    Go gRPC超時(shí)控制Deadlines用法詳解

    這篇文章主要為大家介紹了Go gRPC超時(shí)控制Deadlines用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Golang中堆排序的實(shí)現(xiàn)

    Golang中堆排序的實(shí)現(xiàn)

    堆是一棵基于數(shù)組實(shí)現(xiàn)的特殊的完全二叉樹,本文主要介紹了Golang中堆排序的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 使用GO實(shí)現(xiàn)Paxos共識(shí)算法的方法

    使用GO實(shí)現(xiàn)Paxos共識(shí)算法的方法

    這篇文章主要介紹了使用GO實(shí)現(xiàn)Paxos共識(shí)算法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09

最新評(píng)論