Go Web 編程--如何確保Cookie數據的安全傳輸

什麼是Cookie

Cookie(也叫Web Cookie或瀏覽器Cookie)是服務器發送到用戶瀏覽器並保存在本地的一小塊數據,它會在瀏覽器下次向同一服務器再發起請求時被攜帶併發送到服務器上。一般,它用於告知服務端兩個請求是否來自同一瀏覽器,如保持用戶的登陸狀態。Cookie使基於無狀態的HTTP協議記錄穩定的狀態信息成爲了可能。算法

Cookie主要用於如下三個方面:編程

  • 會話狀態管理(如用戶登陸狀態、購物車、遊戲分數或其它須要記錄的信息)
  • 個性化設置(如用戶自定義設置、主題等)
  • 瀏覽器行爲跟蹤(如跟蹤分析用戶行爲等)

Go語言如何表示Cookie

Gonet/http庫中使用http.Cookie結構體表示一個Cookie數據,調用http.SetCookie函數則會告訴終端用戶的瀏覽器把給定的http.Cookie值設置到瀏覽器Cookie裏,相似下面:跨域

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

http.Cookie結構體類型的定義以下:瀏覽器

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
}
複製代碼

NameValue字段就很少說了,單獨針對幾個須要解釋的字段進行說明。安全

Domain

默認值是當前正在訪問的Host的域名,假設咱們如今正在訪問的是www.example.com,若是須要其餘子域名也可以訪問到正在設置的Cookie值的話,將它設置爲example.com 。注意,只有正在被設置的Cookie須要被其餘子域名的服務訪問到時才這麼設置。bash

c := Cookie{
  ......
  Domain: "example.com",
}
複製代碼

Path

設置當前的 Cookie 值只有在訪問指定路徑時才能被服務器程序讀取。默認爲服務端應用程序上的任何路徑,可是您可使用它限制爲特定的子目錄。例如:服務器

c := Cookie{
  Path: "/app/",
}
複製代碼

Secure

標記爲Secure 的Cookie只應經過被HTTPS協議加密過的請求發送給服務端。但即使設置了 Secure 標記,敏感信息也不該該經過Cookie傳輸,由於Cookie有其固有的不安全性,Secure標記也沒法提供確實的安全保障。從 Chrome 52 和 Firefox 52 開始,不安全的站點(http:)沒法使用CookieSecure 標記。cookie

HttpOnly

爲避免跨域腳本 (XSS) 攻擊,經過JavaScript的API沒法訪問帶有 HttpOnly 標記的Cookie,它們只應該發送給服務端。若是包含服務端Session 信息的Cookie 不想被客戶端JavaScript 腳本調用,那麼就應該爲其設置 HttpOnly 標記。session

安全地傳輸Cookie

接下來咱們探討兩種安全傳輸Cookie的方法併發

對Cookie數據進行數字簽名

對數據進行數字簽名是在數據上添加「簽名」的行爲,以即可以驗證其真實性。不須要對數據進行加密或屏蔽。

簽名的工做方式是經過散列-咱們對數據進行散列,而後將數據與數據散列一塊兒存儲在Cookie中。而後,當用戶將Cookie發送給咱們時,咱們再次對數據進行哈希處理,並驗證其是否與咱們建立的原始哈希匹配。

咱們不但願用戶也用篡改後的數據建立新的哈希,所以常常會看到使用HMAC之類的哈希算法,以即可以使用密鑰對數據進行哈希。這樣能夠防止最終用戶同時編輯數據和數字簽名(哈希)。

JWT也是使用的這種數字簽名的方式進行傳輸的。

上面的數據簽名過程並不須要咱們本身去實現,咱們能夠在Go中使用gorilla/securecookie的程序包來完成此操做,在該程序包中,你能夠在建立SecureCookie時爲其提供哈希密鑰,而後使用該對象來保護你的Cookie

Cookie數據進行簽名:

//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)
  }
複製代碼

解析被簽名的 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)
    }
  }
}
複製代碼

注意這裏的Cookie數據未加密,僅僅是被編碼了,任何人均可以把Cookie數據解碼回來。

加密Cookie 數據

每當將數據存儲在Cookie中時,請始終儘可能減小存儲在Cookie中的敏感數據量。不要存儲用戶密碼之類的東西,並確保任何編碼數據也沒有此信息。在某些狀況下,開發人員在不知不覺中將敏感數據存儲在CookieJWT中,由於它們是base64編碼的,但實際上任何人均可以解碼該數據。它已編碼,未加密。

這是一個很大的錯誤,所以,若是你擔憂意外存儲敏感內容,建議 你使用gorilla/securecookie之類的軟件包。

以前咱們討論瞭如何將其用於對Cookie進行數字簽名,可是securecookie也能夠用於加密和解密Cookie數據,以使其沒法輕鬆解碼和讀取。

要使用該軟件包加密Cookie,只需在建立SecureCookie實例時傳入一個blockKey便可。

將上面簽名Cookie的代碼片斷進行一些小改動,其餘地方徹底不用動,securecookie包會幫助咱們進行Cookie的加密和解密:

var hashKey = securecookie.GenerateRandomKey(64)
var blockKey = securecookie.GenerateRandomKey(32)
var s = securecookie.New(hashKey, blockKey)
複製代碼

總結

今天的文章除了闡述如何使用Go語言安全地傳輸Cookie數據外,再次格外強調一遍,編碼和加密的不一樣,從數據可讀性上看,二者差很少,但本質上是徹底不同的:

  • 編碼使用公開可用的方案將數據轉換爲另外一種格式,以即可以輕鬆地將其反轉。

  • 加密將數據轉換爲另外一種格式,使得只有特定的我的才能逆轉轉換。

咱們在作數據傳輸時必定要記住二者的區別,某種意義上,我以爲記住這兩點的區別比你學會今天文章裏怎麼安全傳輸Cookie更重要。

前文回顧:

深刻學習用Go編寫HTTP服務器

使用gorilla/mux加強Go HTTP服務器的路由能力

十分鐘學會用Go編寫Web中間件

Go Web編程--應用ORM

Go Web編程--深刻學習解析HTTP請求

Go Web編程--使用Go語言建立靜態文件服務器

相關文章
相關標籤/搜索