内容简介:Cookie是指网站为了辨别用户身份而储存于客户端的数据,由网景公司的前雇员卢·蒙特利在1993年3月发明。最初定义于服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态,并且可以基于Cookie实现Session,用来在服务器端存储用户的数据。现在,几乎所有的商业网站都会使用Cookie技术用来标示浏览的用户,比如电子商务中的购物车、广告追踪系统等,并且涉及到一系列的安全问题和隐私问题。
Cookie是指网站为了辨别用户身份而储存于客户端的数据,由网景公司的前雇员卢·蒙特利在1993年3月发明。最初定义于 RFC 2109 , 以及后续的规范 RFC 2965 、 RFC 6265 。
服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态,并且可以基于Cookie实现Session,用来在服务器端存储用户的数据。
现在,几乎所有的商业网站都会使用Cookie技术用来标示浏览的用户,比如电子商务中的购物车、广告追踪系统等,并且涉及到一系列的安全问题和隐私问题。
Go的标准库中提供了Cookie的操作,并且第三方的库提供了Session的实现,所以在使用 Go 开发web应用中,我们可以很方便的实现session的管理,但是也有一些安全方面的设置需要注意。
本文介绍了使用Go语言开发web应用的时候,服务器端Cookie和Session的使用。
Cookie
千言不如一例, 首先我们看一下下面这个例子。这个例子提供了设置Cookie,读取Cookie和删除Cookie的示例。
package main import ( "encoding/json" "flag" "net/http" ) var ( addr = flag.String("addr", ":8080", "server address") ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", index) mux.HandleFunc("/get", getCookie) mux.HandleFunc("/delete", deleteCookie) mux.HandleFunc("/set", setCookie) http.ListenAndServe(*addr, mux) } func index(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`<a href="#" onclick="alert(document.cookie)">Click here!</a>`)) } func getCookie(w http.ResponseWriter, r *http.Request) { c, err := r.Cookie("this_is_a_test_cookie") if err != nil { w.Write([]byte("读取cookie失败: " + err.Error())) } else { data, _ := json.MarshalIndent(c, "", "\t") w.Write([]byte("读取的cookie值: \n" + string(data))) } } func deleteCookie(w http.ResponseWriter, r *http.Request) { c := http.Cookie{ Name: "this_is_a_test_cookie", MaxAge: -1} http.SetCookie(w, &c) w.Write([]byte("cookie已被删除")) } func setCookie(w http.ResponseWriter, r *http.Request) { c := http.Cookie{ Name: "this_is_a_test_cookie", Value: "true", HttpOnly: true, //Secure: true, MaxAge: 300} http.SetCookie(w, &c) w.Write([]byte("cookie已创建\n")) }
主要是围绕 http.Cookie
这个struct的一些方法。
-
读取:
http.Request
的Cookie(name string) (*Cookie, error)
和Cookies() []*Cookie
方法 ,http.Response
的Cookies() []*Cookie
方法。 -
写入:
http.Request
的AddCookie(c *Cookie)
,SetCookie(w ResponseWriter, cookie *Cookie)
函数
从服务器端来看, 我们从 http.Request
中读取客户端传入的Cookie, 或者把Cookie写入到 http.Response
中。
从客户端来看,我们需要把Cookie设置到 http.Request
传给服务器,或者从 http.Response
中读取Cookie。
所以你会看到 Request
和 Response
中都有读取和设置Cookie的方法,只是针对不同的场景而已。
查看下面的Cookie的定义,你也会看到有些字段在读取的时候是空值,这是因为在这个场景下,是没有这个值的,比如服务器端读取Cookie值时,浏览器并不会把Max-Age的传给服务器,所以在服务器端读取这个Cookie指的时候MaxAge值是0。
type Cookie struct { Name string Value string Path string Domain string Expires time.Time RawExpires string MaxAge int Secure bool HttpOnly bool SameSite SameSite Raw string Unparsed []string }
这几个字段要熟悉:
- Name/Value: Cookie的名称和指,cookie中最基本的数据
-
Path: 可以将Cookie限定在某个路径下,只有这个路径和它的子路径才可以访问这个Cookie, 比如Path =
examle.com/abc/
的Cookie,只有examle.com/abc/
和子路径比如examle.com/abc/def
才能访问。默认为当前路径。 -
Domain: 只关联的web服务器的域名, 比如
example.com
。如果你设置的Cookie的domain为a.example.com
,那么访问b.example.com
的时候是不能访问这个Cookie的,如果想访问,那么请将domain设置它们共同的域example.com
。 你能在访问example.com
的时候设置domain为baidu.com
吗? 不行 ,这是出于安全的限制。 默认是当前的域名。 -
Expires: 为过期时间,Cookie超过这个时间点就会被删除了。
-
RawExpires: Expires字符串表示, 格式为
Wdy, DD Mon YYYY HH:MM:SS GMT
或者Wdy, DD Mon YY HH:MM:SS GMT
,在读取Cookie时候会被设置 -
MaxAge: 最大存活时间,单位是秒,-1为删除这个Cookie, 0是不设置Max-Age, 正数为存活的秒数。你可以查看这个 测试页 了解不同的设置的效果。
如果同时设置了 Expires
和 MaxAge
,以 Max-Age
为准。
-
Secure: 设置 Cookie 只在确保安全的请求中才会发送。当请求是 HTTPS 或者其他安全协议时,包含 secure 选项的 Cookie 才能被保存到浏览器或者发送至服务器。
-
HttpOnly: 这个选项用来设置 Cookie 是否能通过 js 去访问。强烈建议设置这个值为true,否则容易被XSS等攻击。你可以把上面的例子这个字段注释掉,访问首页的时候点击连接为显示当前页面的Cookie的值,这只是用来测试,要是有一段javascript把你的cookie传到第三方网站危险就大了。
-
SameSite: 2016年Chrome中加入的一个新属性,避免在跨域(XSRF)访问的时候把Cookie传给第三方网站。
Cookie的缺陷
Cookie也有一些天生的缺陷。
- Cookie会被附加在每个HTTP请求中,所以无形中增加了流量。
- 由于在HTTP请求中的Cookie是明文传递的,很容易遭受到中间人的攻击,所以安全性成问题,除非用HTTPS。
- Cookie的大小限制在4KB左右,对于复杂的存储需求来说是不够用的。
- 不同的浏览器Cookie不是共享的,你在Chrome中登录了一个网站,使用Firefox还需要再次登录,因为这两个浏览器的Cookie不共享。
- 同一台机器同一个浏览器会共享同一个Cookie池,A用完浏览器后,如果不清理Cookie, B使用的时候会得到A的Cookie。
- Cookie存在本地是明文访问的,其他用户如果能访问的这个Cookie,就能看到这个Cookie的内容
- 容易遭受XSS跨站访问
- 第三方脚本追踪。网站中嵌入第三方的代码,就容易被第三方公司利用,在一些互联网巨头和广告公司中经常会使用。你在A网站浏览一些商品,在浏览B网站的时候,B网站的广告会给你推送这些类似商品的信息,这是因为A和B都嵌入了同一个第三方公司的代码,通过Cookie能追踪到你的浏览记录。
- Cookie投毒攻击,例如在一个购物网站的Cookie中包含了顾客应付的款项,攻击者将该值改小,达到少付款的目的。
所以在使用Cookie的时候,需要一些额外的措施,避免收到攻击,下面是一些推荐设置:
- 设置合理的domain和path
- 设置合适的MaxAge, 不使用时或者推出时设置为-1
- 设置HttpOnly为true
- 设置SameSite
- 采用https, 设置Secure为true
- cookie不存储私密的东西,名称不设置直观易读的名称
- cookie进行加盐和加密
- 不设置太大的Cookie
- 设置到安全较高的操作时,服务器端对cookie和客户端ID(浏览器属性、操作系统、客户端IP)进行验证,避免被人窃取cookie
加密Cookie
涉及到私密的数据的时候,可以采取服务器加密的方式,在服务器先进行加密,再设置Cookie。你可以使用 securecookie 实现这个功能。
// Hash keys should be at least 32 bytes long var hashKey = []byte("very-secret") // Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long. // Shorter keys may weaken the encryption used. var blockKey = []byte("a-lot-secret") var s = securecookie.New(hashKey, blockKey) func SetCookieHandler(w http.ResponseWriter, r *http.Request) { value := map[string]string{ "foo": "bar", } if encoded, err := s.Encode("cookie-name", value); err == nil { cookie := &http.Cookie{ Name: "cookie-name", Value: encoded, Path: "/", Secure: true, HttpOnly: true, } http.SetCookie(w, cookie) } } func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { if cookie, err := r.Cookie("cookie-name"); err == nil { value := make(map[string]string) if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil { fmt.Fprintf(w, "The value of foo is %q", value["foo"]) } } }
hashKey
用来生成Cookie值的摘要(hmac),可以验证数据是否被篡改, blockKey
是可选的,可以用来加密cookie的值。
cookie值可以是任意的对象,默认使用gob进行序列化,当然也可以配置使用json格式。
使用起来很方便,只是多一步 Encode
/ Decode
的过程。
它还提供了另一个好处,就是可以在服务器端验证Cookie的设置日期,在服务器端也进行MaxAge的校验。
Session
Cookie将数据存放在客户端,并且还有4k的大小的限制,为了更好的和用户进行交互,很多编程语言的开发框架提供了session的功能。
Session
还是基于cookie实现的(当然在禁用cookie的情况下可以在url后加后缀的方式曲折的实现)。一个session对应一个sessionid, 可以将这个sessionid作为cookie设置到客户端,服务器端建立一个 sessionid <---> session
的对应结构。 浏览器将sessionid发送给客户端的时候,服务器根据这个id得到session对象,就可以存取这个sessoin的内容。
可以将session放在内容中,也可以放在中心服务器如 memcached 、 redis 、 mysql 中,设置可以在web服务器中同步,这样可以实现有状态的session负载均衡。
sessionid要随机化,否则如果被人猜中的话,可以通过伪造session id冒充用户。
sessionid在cookie中的名称,不同的编程语言/web框架各不相同。
比如 php 使用PHPSESSID, java使用JSESSIONID, ColdFusion使用CFID & CFTOKEN, asp.net使用ASP.NET_SessionId, 通过cookie中的sessionid的名称,我们大致能推断出服务器所使用的编程语言。如果你不想暴露服务器的技术栈,你可以使用通用的名称,比如 id
。
Go中有多个第三方的session实现,最常用的是 gorilla/sessions , 它可以用在其他的go的web框架中,并且有一二十个不同存储的实现,可以实现分布式的session。
使用起来也很方便:
import ( "net/http" "github.com/gorilla/sessions" ) var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY")) func MyHandler(w http.ResponseWriter, r *http.Request) { // 得到一个session session, _ := store.Get(r, "session-name") // 设置session的一些值 session.Values["foo"] = "bar" session.Values[42] =43 // 在返回之前保存它 session.Save(r, w) }
如果不使用 gorilla/mux
框架,你需要 context.ClearHandler
包装你的handler,否则会出现内存泄漏。
beego中也实现了一个独立的session模块, 最近几天, fasthttp作者也实现了一个session库,当然秉承他的理念,性能是第一位的, 有兴趣的同学也可以关注下 fasthttp/session 。
JWT
对于访问认证来说,为每个客户端提供一个session对象,这对于用户访问巨大的网站来说,这是相当奢侈的。那么能不能提供一种机制,把用户的访问权限放在客户端,但是又能保证客户端的数据不被篡改?
类似securecookie机制,目前正在流行一种JWT的认证方式。
你可以搜索一些相关的介绍,比如 什么是 JWT -- JSON WEB TOKEN 、 JSON Web Token 入门教程 。
Go也有jwt的库可以使用, 比较有名的是 jwt-go 。
参考文档
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
- https://www.sohamkamani.com/blog/2017/01/08/web-security-session-cookies/
- https://stackoverflow.com/questions/17834144/how-tomcat-handles-session-internally
- https://docs.spring.io/spring-session/docs/1.3.3.RELEASE/reference/html5/guides/custom-cookie.html#custom-cookie-spring-configuration
- https://stackoverflow.com/questions/595872/under-what-conditions-is-a-jsessionid-created
- https://www.jianshu.com/p/e8736aa3be2b
- https://www.cnblogs.com/doit8791/p/5926575.html
- https://harttle.land/2015/08/10/cookie-session.html
- https://mrcoles.com/blog/cookies-max-age-vs-expires/
- https://github.com/boj/redistore
- https://github.com/gorilla/sessions
- https://github.com/gorilla/securecookie
- https://github.com/fasthttp/session
- https://www.sohamkamani.com/blog/2018/03/25/golang-session-authentication/
- https://www.calhoun.io/securing-cookies-in-go/
- https://medium.com/@sherryhsu/session-vs-token-based-authentication-11a6c5ac45e4
- https://www.owasp.org/index.php/Session_Management_Cheat_Sheet
- https://en.wikipedia.org/wiki/HTTP_cookie#Cookie_theft_and_session_hijacking
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- ABP开发框架前后端开发系列---(14)基于Winform的ABP快速开发框架
- Java开发人员的Flex开发
- Java开发人员的Flex开发
- 行为驱动开发让开发做正确事
- 让开发者专注于应用开发,OpenCenter 3.0 开发者预览版发布
- 让开发者专注于应用开发,OpenCenter 3.0 开发者预览版发布
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
XML、JSON 在线转换
在线XML、JSON转换工具
Markdown 在线编辑器
Markdown 在线编辑器