How to ensure the secure transfer of Cookie data in Go language

  • 2020-10-07 18:42:49
  • OfStack

What is a Cookie

Cookie (also known as Web Cookie or Browser Cookie) is a small piece of data sent by the server to the user's browser and stored locally, which is carried and sent to the server the next time the browser makes another request to the same server. Typically, it is used to tell the server whether two requests are coming from the same 1 browser, such as keeping the user logged in. Cookie makes it possible for the stateless HTTP protocol to record stable state information.

Cookie is mainly used in the following three aspects:

Session state management (such as user login status, shopping cart, game score, or other information to be recorded) Personalization Settings (such as user-defined Settings, themes, etc.) Browser behavior tracking (such as tracking and analyzing user behavior)

How does Go mean Cookie

In Go's net/http library, the http.Cookie structure is used to represent 1 Cookie data. Calling the http.SetCookie function will tell the end user's browser to set the given http.Cookie value to browser Cookie, similar to the following:


func someHandler(w http.ResponseWriter, r *http.Request) {
 c := http.Cookie{
  Name: "UserName",
  Value: "Casey",
 }
 http.SetCookie(w, &c)
}

http. Cookie structural types are defined as follows:


type Cookie struct {
  Name string
  Value string

  Path    string  // optional
  Domain   string  // optional
  Expires  time.Time // optional
  RawExpires string  // for reading cookies only

  // MaxAge=0 means no 'Max-Age' attribute specified.
  // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
  // MaxAge>0 means Max-Age attribute present and given in seconds
  MaxAge  int
  Secure  bool
  HttpOnly bool
  SameSite SameSite
  Raw   string
  Unparsed []string // Raw text of unparsed attribute-value pairs
}

The Name and Value fields are not explained much, but are described separately for a few fields that need to be explained.

Domain

The default value is the domain name of the currently accessed Host, assuming that we are accessing www.example.com, set to example.com if we need other subdomains to also have access to the Cookie value that we are setting. Note that this is only done when the Cookie being set needs to be accessed by other subdomain services.


c := Cookie{
 ......
 Domain: "example.com",
}

Path

Sets the current Cookie value to be read by the server program only when the specified path is accessed. The default is any path on the server application, but you can use it to restrict it to a specific subdirectory. Such as:


c := Cookie{
 Path: "/app/",
}

Secure

Cookie, marked Secure, should only be sent to the server through requests encrypted by the HTTPS protocol. However, even if the Secure tag is set, sensitive information should not be transmitted through Cookie because Cookie is inherently insecure and the Secure tag does not provide a firm guarantee of security. Starting with Chrome 52 and Firefox 52, insecure sites (http :) cannot use Cookie's Secure tag.

HttpOnly

To avoid cross-domain scripting (XSS) attacks, API via JavaScript cannot access Cookie with the HttpOnly tag, which should only be sent to the server. If Cookie containing the server Session information does not want to be called by the client JavaScript script, then the HttpOnly flag should be set for it.

Securely transmit the Cookie

Next we explore two ways to safely transport Cookie

Digitally sign Cookie data

Signing data digitally is the act of adding a "signature" to the data so that its authenticity can be verified. No need to encrypt or mask the data.

The signature works by hashing - we hash the data and then store the data in Cookie starting with the data hashing 1. Then, when the user sends us Cookie, we hash the data again and verify that it matches the original hash we created.

We don't want users to create new hashes with tampered data as well, so we often see hash algorithms such as HMAC used to hash data using keys. This prevents the end user from editing both the data and the digital signature (hash).

JWT is also transmitted using this form of digital signature.

The above data signing process does not require us to implement it ourselves. We can do this in Go using the gorilla/securecookie package, where you can provide SecureCookie with a hash key when creating SecureCookie and then use that object to protect your Cookie.

Signature of Cookie data:


//var s = securecookie.New(hashKey, blockKey)
var hashKey = securecookie.GenerateRandomKey(64)
var s = securecookie.New(hashKey, nil)

func SetCookieHandler(w http.ResponseWriter, r *http.Request) {
 encoded, err := s.Encode("cookie-name", "cookie-value")
 if err == nil {
  cookie := &http.Cookie{
   Name: "cookie-name",
   Value: encoded,
   Path: "/",
  }
  http.SetCookie(w, cookie)
  fmt.Fprintln(w, encoded)
 }

Resolve the signed Cookie:


func ReadCookieHandler(w http.ResponseWriter, r *http.Request) {
 if cookie, err := r.Cookie("cookie-name"); err == nil {
  var value string
  if err = s.Decode("cookie-name", cookie.Value, &value); err == nil {
   fmt.Fprintln(w, value)
  }
 }
}

Note that the Cookie data here is not encrypted, it is simply encoded, and anyone can decode the Cookie data back.

Encrypt Cookie data

Whenever you store data in Cookie, always minimize the amount of sensitive data stored in Cookie. Don't store things like user passwords, and make sure that any encoded data doesn't have this information. In some cases, developers are unknowingly storing sensitive data in Cookie or JWT because they are encoded in base64, but anyone can actually decode that data. It is encoded, not encrypted.

This is a big mistake, so if you are worried about accidentally storing sensitive content, it is recommended that you use a package such as gorilla/securecookie.

We discussed earlier how it can be used to digitally sign Cookie, but securecookie can also be used to encrypt and decrypt Cookie data so that it cannot be easily decoded and read.

To encrypt Cookie using this package, you simply pass in 1 blockKey when you create an instance of SecureCookie.

Make some minor changes to the code snippet of Cookie signed above, and leave everything else untouched. The securecookie package will help us encrypt and decrypt Cookie:


var hashKey = securecookie.GenerateRandomKey(64)
var blockKey = securecookie.GenerateRandomKey(32)
var s = securecookie.New(hashKey, blockKey)

conclusion

In addition to explaining how to securely transmit Cookie data using Go language, today's article emphasizes once again the difference between encoding and encryption. In terms of data readability, the two are similar, but fundamentally different:

The encoding USES a publicly available scheme to convert the data to another format so that it can be easily reversed. Encryption converts the data to another format, so that only certain individuals can reverse the conversion.

We have to remember the difference between the two when we do data transfer, and in a sense, I think it's more important to remember the difference between the two than it is for you to learn how to safely transfer Cookie in today's article.


Related articles: