go cookie学习
cookie的概念
cookie是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息,用于服务器记录客户端的状态。
cookie的作用
解决每个http请求相互独立问题。这是因为HTTP 1.x是无状态的协议,它的每个请求都是完全独立的。当然解决这个问题也可以使用sessio,这里不说session,只说cookie。
cookie解决的过程
携带cookie的HTTP响应体一种方式为:
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Date: Sat, 21 Aug 2021 14:18:41 GMT
Set-Cookie: myname1=mykey1
浏览器收到服务器返回数据,发现请求头中有一个:Set-Cookie,然后它就把这个Cookie保存起来,下次浏览器再请求服务器的时候,会把Cookie也放在请求头中传给服务器:
Host: localhost:8080
Cookie: myname1=mykey1
cookie的属性
我们看下面的图。发现cookie拥有的属性有:Name、Value、Domain、Path、Expires/Max-Age、Size、HTTP、Secure。
属性介绍
1. Name&Value
Name表示Cookie的名称,服务器就是通过name属性来获取某个Cookie值。
Value表示Cookie 的值,大多数情况下服务器会把这个value当作一个key去缓存中查询保存的数据。
2.Domain&Path
Domain表示可以访问此cookie的域名。比如这里是localhost域,因此其他非localhost的域,在发起请求的时候,不能携带这个cookie。如果想要跨域携带,可以:如域A为t1.test.com,域B为t2.test.com,那么在域A生产一个令域A和域B都能访问的cookie就要将该cookie的domain设置为.test.com;
Path表示可以访问此cookie的页面路径。 比如path=/test,那么只有/test(包括/test子目录,比如:/test/a)路径下的页面可以读取此cookie。
3.Expires/Max-Age
Expires/Max-Age表示此cookie超时时间。若设置其值为一个时间,那么当到达此时间后,此cookie失效。不设置的话默认值是Session,意思是cookie会和session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效。
4.Size
Size表示Cookie的name+value的字符数,比如又一个Cookie:id=666,那么Size=2+3=5 。
5.HTTP
HTTP表示cookie的httponly属性。若此属性为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie。
设计该特征意在提供一个安全措施来帮助阻止通过Javascript发起的跨站脚本攻击(XSS)窃取cookie的行为
6.Secure
Secure表示是否只能通过https来传递此条cookie。不像其它选项,该选项只是一个标记并且没有其它的值。
golang中cookie的属性
-
go cookie struct
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 }
- 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
。 - Expires: 指定cookie到什么时候过期时间,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, 正数为存活的秒数。
- Secure: 设置 Cookie 只在确保安全的请求中才会发送。当请求是 HTTPS 或者其他安全协议时,包含 secure 选项的 Cookie 才能被保存到浏览器或者发送至服务器。
- HttpOnly: 这个选项用来设置 Cookie 是否能通过 js 去访问。强烈建议设置这个值为true,否则容易被XSS等攻击。你可以把上面的例子这个字段注释掉,访问首页的时候点击连接为显示当前页面的Cookie的值,这只是用来测试,要是有一段javascript把你的cookie传到第三方网站危险就大了
- SameSite: 2016年Chrome中加入的一个新属性,避免在跨域(XSRF)访问的时候把Cookie传给第三方网站。
代码实现
-
设置cookie
package main import ( "fmt" "net/http" ) func main(){ fmt.Println("创建cookie") http.HandleFunc("/setCookie1",setCookie1) http.HandleFunc("/setCookie2",setCookie2) http.HandleFunc("/sendData",sendData) http.ListenAndServe(":8080",nil) } // 设置cookie值 func setCookie1(w http.ResponseWriter,r *http.Request){ c:=http.Cookie{Name:"myname1",Value:"mykey1"} http.SetCookie(w,&c) fmt.Fprintln(w,"无名之辈") } // 设置cookie值 func setCookie2(w http.ResponseWriter,r *http.Request){ c:=http.Cookie{Name:"myname2",Value:"mykey2"} http.SetCookie(w,&c) fmt.Fprintln(w,"无名之辈2") } func sendData(w http.ResponseWriter,r *http.Request){ fmt.Fprintln(w,"海贼王") }
-
问题思考:
-
服务端每次响应时是否要都要设置cookie的请求头?
答:不用。首先,在第一次访问之后,服务端设置的cookie字段,发送出去之后,便可以在下一次访问需要有cookie的接口的时候,服务端不需要再设置cookie。只需要判断cookie的有效性就行
-
同个域名下,不同的处理函数中,同时都设置cookie,会发生什么样的结果?
-
cookie会以逗号隔开连接在一起
-
访问上面两个设置cookie接口,然后再访问sendData接口的cookie结果
-
-
-
-
获取cookie
- 有两个方法:cookies()获取cookie数组,cookie(cookieName)获取指定的名字的cookie的value
package main import ( "fmt" "net/http" ) func main(){ fmt.Println("创建cookie") http.HandleFunc("/setCookie1",setCookie1) http.HandleFunc("/setCookie2",setCookie2) http.HandleFunc("/sendData",sendData) http.HandleFunc("/getCookie",getCookie) http.ListenAndServe(":8080",nil) } func setCookie1(w http.ResponseWriter,r *http.Request){ c:=http.Cookie{Name:"myname1",Value:"mykey1"} http.SetCookie(w,&c) fmt.Fprintln(w,"无名之辈") } func setCookie2(w http.ResponseWriter,r *http.Request){ c:=http.Cookie{Name:"myname2",Value:"mykey2"} http.SetCookie(w,&c) fmt.Fprintln(w,"无名之辈2") } func sendData(w http.ResponseWriter,r *http.Request){ fmt.Fprintln(w,"海贼王") } // 获取cookie func getCookie(w http.ResponseWriter,r *http.Request){ var data string for i:=0;i<len(r.Cookies());i++{ fmt.Println(r.Cookies()[i]) data = data + r.Cookies()[i].String() } c,_:=r.Cookie("myname2") data = data + c.String() fmt.Fprintln(w,data) }
cookie利用
- 利用cookie发送一次性数据
package main
import (
"fmt"
"net/http"
)
func main(){
fmt.Println("创建cookie")
http.HandleFunc("/setCookie1",setCookie1)
http.HandleFunc("/setCookie2",setCookie2)
http.HandleFunc("/sendData",sendData)
http.HandleFunc("/getCookie",getCookie)
http.ListenAndServe(":8080",nil)
}
func setCookie1(w http.ResponseWriter,r *http.Request){
c:=http.Cookie{Name:"myname1",Value:"mykey1"}
http.SetCookie(w,&c)
fmt.Fprintln(w,"无名之辈")
}
func setCookie2(w http.ResponseWriter,r *http.Request){
c:=http.Cookie{Name:"myname2",Value:"mykey2"}
http.SetCookie(w,&c)
fmt.Fprintln(w,"无名之辈2")
}
func sendData(w http.ResponseWriter,r *http.Request){
_,err1:= r.Cookie("myname1")
_,err2:= r.Cookie("myname2")
if err1!=nil && err2!=nil{
fmt.Fprintln(w,"no message to show")
return
}else{
// 将所有的cookie设置过期
expireCookies := http.Cookie{
Name:"myname1",
MaxAge:-1,
}
expireCookies1 := http.Cookie{
Name:"myname2",
MaxAge:-1,
}
http.SetCookie(w,&expireCookies)
http.SetCookie(w,&expireCookies1)
fmt.Fprintln(w,"海贼王")
}
}
func getCookie(w http.ResponseWriter,r *http.Request){
var data string
for i:=0;i<len(r.Cookies());i++{
fmt.Println(r.Cookies()[i])
data = data + r.Cookies()[i].String()
}
c,_:=r.Cookie("myname2")
data = data + c.String()
fmt.Fprintln(w,data)
}
- 验证方式
- 分别访问一次setCookie1和setCookie2接口,再访问sendData接口。
- 再次访问sendData接口
cookie文件查找
cookie的保存,就是将cookie的值写入到客户端的cookie文件中。这里介绍怎么从chrome中去查看cookie.txt。
1、在chrome的输入框中输入:chrome://settings/siteData。
2、根据host找到cookie文件。
根据刚才找到的文件,打开就可以看到cookie拥有的属性了。如下:
注:这里的脚本可访问如果为是,则可以使用document.cookie()或者其他脚本获取到。如果不是,则只能通过HTTP传递获取。
cookie特点
优点
- 不需要任何服务器资源,因为cookie是存在客户端并发送给服务端读取的
- 可配置到期规则,控制cookie的生命周期,使之不会永远生效,偷盗者可能拿到的是一个过期的cookie
缺点
- Cookie会被附加在每个HTTP请求中,所以无形中增加了流量。
- 由于在HTTP请求中的Cookie是明文传递的,很容易遭受到中间人的攻击,所以安全性成问题,除非用HTTPS。
- Cookie的大小限制在4KB左右,对于复杂的存储需求来说是不够用的。
- 不同浏览器不能共享
- 容易遭受XSS跨站访问
- Cookie投毒攻击,例如在一个购物网站的Cookie中包含了顾客应付的款项,攻击者将该值改小,达到少付款的目的。
使用注意事项
- 设置合理的domain和path
- 设置合适的MaxAge, 不使用时或者推出时设置为-1
- 设置HttpOnly为true
- 设置SameSite。可以参考:https://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html
- 采用https, 设置Secure为true
- 设置到安全较高的操作时,服务器端对cookie和客户端ID(浏览器属性、操作系统、客户端IP)进行验证,避免被人窃取cookie
参考文档