cookie是什么
首先需要明白的是兄一,
- cookie是儲(chǔ)存在瀏覽器中的一段字符串溺欧,它本身是沒有任何危害的,不包含任何可執(zhí)行的代碼。
- 儲(chǔ)存cookie是瀏覽器的功能官册,瀏覽器的安裝目錄下會(huì)有一個(gè)專門存放不同域名下的cookie的文件夾。
- 當(dāng)網(wǎng)頁發(fā)起http請(qǐng)求的時(shí)候瞒大,瀏覽器首先會(huì)檢查是否有對(duì)應(yīng)域名的cookie拣度,如果有,就會(huì)自動(dòng)添加到request header中蝇狼,發(fā)送給服務(wù)器阅畴。
- cookie的存放有大小限制,不可以超過4kb, 每一個(gè)域名下的cookie數(shù)量最多是20個(gè)
圖解cookie的運(yùn)行機(jī)制
我們以百度登錄的cookie為例迅耘,當(dāng)我們沒有登錄前贱枣,給www.baidu.com發(fā)送http請(qǐng)求监署,返回的數(shù)據(jù)
當(dāng)我們登陸后,再訪問百度的域名后
基本通訊流程: 登錄的時(shí)候纽哥,百度服務(wù)器會(huì)response header會(huì)返回一個(gè)不同用戶的一個(gè)標(biāo)識(shí)符(cookie) => 登錄完成后钠乏,訪問百度的主頁的時(shí)候,瀏覽器會(huì)自動(dòng)在request header帶上剛剛存放的cookie去請(qǐng)求百度的服務(wù)器 => 百度服務(wù)器接收到對(duì)應(yīng)的cookie后春塌,返回不同用戶的數(shù)據(jù)給瀏覽器晓避。
cookie的格式
獲取cookie
JS原生的api提供了獲取cookie的方法,document.cookie
(注意摔笤,這個(gè)方法只能獲取非 HttpOnly 類型的cookie
)
打印出的結(jié)果是一個(gè)字符串類型够滑,因?yàn)?code>cookie本身就是存儲(chǔ)在瀏覽器中的字符串。但這個(gè)字符串是有格式的吕世,由鍵值對(duì) key=value
構(gòu)成彰触,鍵值對(duì)之間由一個(gè)分號(hào)
和一個(gè)空格
隔開。
cookie的參數(shù)
- expires 解釋cookie在什么時(shí)間段內(nèi)有效
- domain和path 限制cookie能被那些網(wǎng)站訪問
-
secure 設(shè)置
cookie
只在確保安全的請(qǐng)求中才會(huì)發(fā)送(https等安全協(xié)議) - httpOnly 設(shè)置cookie能否通過js來訪問命辖,擁有httpOnly的cookie只能被服務(wù)器訪問况毅,客戶端不能訪問,可以用來防止XSS腳本攻擊
實(shí)例操作cookie
靜態(tài)服務(wù)器的搭建
// 安裝依賴
mkdir demo-cookie && cd demo-cookie
npm init -y
npm install --save express
// 監(jiān)聽本機(jī)3000端口
const express = require('express')
const app = express()
app.listen(3000, err => {
if (err) {
return console.log(err)
}
console.log('---- 打開 http://localhost:3000 吧----')
})
設(shè)置cookie
// 我們?cè)诟夸浬厦嬖O(shè)置cookie
app.get('/', (req, res) => {
res.cookie('name','hcc')
res.send(`<h1>hello world!</h1>`)
})
app.get('/user', (req, res) => {
res.send('<h1>hello world user!</h1>')
})
可以看到響應(yīng)頭response header中有Set Cookie
設(shè)置了我們剛剛在服務(wù)器中寫的對(duì)應(yīng)的cookie
尔艇,通過瀏覽器的Application 中的cookie我們可以看到已經(jīng)存放了我們剛剛寫的cookie
尔许。
設(shè)置多個(gè)cookie值
app.get('/', (req, res) => {
res.cookie('name','hcc')
res.cookie('name','yx')
res.cookie('age',24)
res.send(`<h1>hello world!</h1>`)
})
app.get('/user', (req, res) => {
res.send('<h1>hello world user!</h1>')
})
我們可以看到response header中會(huì)出現(xiàn)多個(gè)set cookie,來對(duì)應(yīng)我們?cè)诜?wù)器中設(shè)置的cookie值
通過document.cookie
獲取我們?cè)O(shè)置的cookie
的值终娃,可以看出味廊,重名的cookie會(huì)被后一個(gè)值覆蓋,不同的cookie以分號(hào)結(jié)尾棠耕,中間有一個(gè)空格
document.cookie //
"name=yx; age=24"
expires和maxAge余佛、httpOnly
app.get('/', (req, res) => {
res.cookie('expires','5秒后消失',{
expires: new Date(Date.now()+5000)
})
res.cookie('max-age','timeout',{
maxAge: 5000
})
res.cookie('httpOnly','set',{
httpOnly:true
})
res.cookie('name','yx')
res.cookie('age',24)
res.send(`<h1>hello world!</h1>`)
})
app.get('/user', (req, res) => {
res.send('<h1>hello world user!</h1>')
})
我們分別給不同的cookie設(shè)置了時(shí)間限制和httpOnly,讓我們看看他們?cè)跒g覽器中的展示
expires
和maxAge
上面已經(jīng)講述了窍荧,是用來設(shè)置cookie的有效時(shí)間的辉巡,maxAge的使用更加的方便和普遍,因?yàn)?code>expires的時(shí)間限制是格林威治時(shí)間蕊退,寫起來不方便郊楣,maxAge
是根據(jù)你的當(dāng)前時(shí)間+ 設(shè)置的時(shí)間(毫秒),更加的方便瓤荔。
下面分別獲取5秒前的cookie和5秒后的cookie
// 5秒前
document.cookie
"expires=10%E7%A7%92%E5%90%8E%E6%B6%88%E5%A4%B1; max-age=timeout; name=yx; age=24"
// 5秒后
document.cookie
"name=yx; age=24"
細(xì)心的同學(xué)已經(jīng)發(fā)現(xiàn)了净蚤,我們之前設(shè)置了httpOnly=set
的cookie值,但是通過document.cookie
并沒有獲取到输硝,我們看看瀏覽器是否已經(jīng)存放了塞栅,下圖展示了,瀏覽器已經(jīng)存放了我們?cè)O(shè)置的httpOnly的cookie
,并且后面還有一個(gè)勾出現(xiàn),好像就是通過客戶端不可以輸出查看放椰。
如果服務(wù)器在設(shè)置cookie
的時(shí)候,添加了額外的屬性httpOnly
的話愉粤,就是限制了不能通過瀏覽器獲得砾医,這個(gè)對(duì)于防止攻擊很有用。
額外的衣厘,當(dāng)你5秒后刷新瀏覽器存放的cookie值如蚜,我們剛剛設(shè)置的時(shí)間限制的cookie都會(huì)被清除
刪除cookie
只需要將對(duì)應(yīng)的name
的cookie的時(shí)間限制設(shè)置為0,就會(huì)被瀏覽器清除
let removeCookie = (name, path, domain) => {
document.cookie = `${name}=; path=${path}; domain=${domain}; max-age=0`
}
子作用域
首先有一個(gè)大的概念影暴,就像繼承家產(chǎn)一樣错邦,父親的會(huì)給兒子使用,但是兒子的不會(huì)給父親使用型宙。
當(dāng) Cookie 的 domain 為 news.163.com
撬呢,那么訪問 news.163.com
的時(shí)候就會(huì)帶上 Cookie;
當(dāng) Cookie 的 domain 為 163.com
妆兑,那么訪問 news.163.com
和 sports.163.com
就會(huì)帶上 Cookie
實(shí)例
下面我們?cè)O(shè)置了一個(gè)根域名和一個(gè)根域名下面的子路徑的請(qǐng)求
app.get('/', (req, res) => {
res.cookie('httpOnly','set',{
httpOnly:true
})
res.cookie('name','yx')
res.cookie('age',24)
res.send(`<h1>hello world!</h1>`)
})
app.get('/user', (req, res) => {
res.cookie('child','user')
res.cookie('user','hcc',{
path: '/user',
httpOnly: true
})
res.send('<h1>hello world user!</h1>')
})
訪問根域名下面的cookie存放
訪問/user下面的cookie存放
很清晰的發(fā)現(xiàn)魂拦,我們?cè)?user下面設(shè)置的cookie并不會(huì)存放在根域名/下,但是我們?cè)?設(shè)置的cookie,會(huì)存放在/user下面搁嗓。
CSRF攻擊
CSRF(cross-site request forgery),中文名:跨站請(qǐng)求偽造芯勘,也被稱為:one click attack/session riding
(很形象), 縮寫為 CSRF/XSRF
,就是你點(diǎn)擊一個(gè)什么按鈕腺逛,會(huì)觸發(fā)一定的程序攻擊荷愕。
CSRF有什么危害
CSRF可以理解為,在你不知情的情況下棍矛,攻擊者盜用你的身份安疗,惡意的像服務(wù)器發(fā)送請(qǐng)求。
- 以你的名義發(fā)送垃圾郵箱
- 購買商品
- 虛擬貨幣轉(zhuǎn)賬
CSRF的原理
從圖中我們可以看出茄靠,一個(gè)CSRF攻擊需要2個(gè)條件
- 登錄了一個(gè)受信任的網(wǎng)站A茂契,并且本地存放了cookie
- 在不關(guān)閉A的情況下,訪問了危險(xiǎn)網(wǎng)站B
你也可以自己做的一下要求來防止CSRF攻擊慨绳,但是樓主是做不到的
- 你不能保證你登陸了一個(gè)網(wǎng)站后掉冶,不再打開一個(gè)tab 頁面并訪問另外的網(wǎng)站。(習(xí)慣)
- .你不能保證你關(guān)閉瀏覽器后脐雪,你本地的cookie 馬上過期厌小,你上次的會(huì)話已經(jīng)結(jié)束 (服務(wù)器)
實(shí)例
實(shí)例一
銀行網(wǎng)站A,它以GET請(qǐng)求來完成銀行轉(zhuǎn)賬的操作战秋,如:http://www.mybank.com/Transfe...
危險(xiǎn)網(wǎng)站B璧亚,它里面有一段HTML的代碼如下:
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
首先,你登錄了銀行網(wǎng)站A脂信,然后訪問危險(xiǎn)網(wǎng)站B癣蟋,噢透硝,這時(shí)你會(huì)發(fā)現(xiàn)你的銀行賬戶少了1000塊。疯搅。濒生。
為什么會(huì)這樣呢?
原因是銀行網(wǎng)站A違反了
HTTP
規(guī)范幔欧,使用GET
請(qǐng)求更新資源罪治。在訪問危險(xiǎn)網(wǎng)站B的之前,你已經(jīng)登錄了銀行網(wǎng)站A礁蔗,而B中的<img>以GET的方式請(qǐng)求第三方資源(這里的第三方就是指A網(wǎng)站了觉义,原本這是一個(gè)合法的請(qǐng)求,但這里被不法分子利用了)浴井,所以你的瀏覽器會(huì)帶上你的銀行網(wǎng)站A的Cookie
發(fā)出GET
請(qǐng)求晒骇,去獲取資源http://www.mybank.com/Transfer.php?toBankId=11&money=1000
,結(jié)果銀行網(wǎng)站服務(wù)器收到請(qǐng)求后滋饲,認(rèn)為這是一個(gè)更新資源操作(轉(zhuǎn)賬操作)厉碟,所以就立刻進(jìn)行轉(zhuǎn)賬操作......
實(shí)例二:
為了杜絕上面的問題,銀行決定改用POST請(qǐng)求完成轉(zhuǎn)賬操作屠缭。但是釣魚網(wǎng)站也意識(shí)到銀行應(yīng)該不會(huì)那么傻逼箍鼓,也與時(shí)俱進(jìn),釣魚網(wǎng)站B的WEB表單如下:
<html>
<head>
<script type="text/javascript">
function steal()
{
iframe = document.frames["steal"];
iframe.document.Submit("transfer");
}
</script>
</head>
<body onload="steal()">
<iframe name="steal" display="none">
<form method="POST" name="transfer" action="http://www.myBank.com/Transfer.php">
<input type="hidden" name="toBankId" value="11">
<input type="hidden" name="money" value="1000">
</form>
</iframe>
</body>
</html>
如果用戶仍是繼續(xù)上面的操作呵曹,很不幸款咖,結(jié)果將會(huì)是再次不見1000塊......因?yàn)檫@里危險(xiǎn)網(wǎng)站B暗地里發(fā)送了POST請(qǐng)求到銀行!
我們看出來,CSRF攻擊是源于WEB的隱式身份驗(yàn)證機(jī)制奄喂!WEB的身份驗(yàn)證機(jī)制(cookie
)雖然可以保證一個(gè)請(qǐng)求是來自于某個(gè)用戶的瀏覽器铐殃,但卻無法保證該請(qǐng)求是用戶批準(zhǔn)發(fā)送的!
CSRF防御
服務(wù)端的CSRF方式方法很多樣跨新,但總的思想都是一致的富腊,就是在客戶端頁面增加偽隨機(jī)數(shù)
-
驗(yàn)證 HTTP Referer 字段
-
在請(qǐng)求地址中添加 token 并驗(yàn)證
xss攻擊
xss(cross site scripting)跨網(wǎng)頁腳本攻擊,是以前常見的一種攻擊方式域帐,主要是通過這幾個(gè)步驟來實(shí)現(xiàn):
- 攻擊者通過頁面惡意的編寫一下腳本語言赘被, 然后發(fā)送給服務(wù)器
- 服務(wù)器接收到攻擊者發(fā)送的請(qǐng)求,但是沒有處理肖揣。直接保存在數(shù)據(jù)庫中民假。
- 當(dāng)用戶訪問同一個(gè)網(wǎng)站的時(shí)候,由于服務(wù)器并沒有處理攻擊者發(fā)送的惡意腳步龙优,所有會(huì)把惡意代碼和原始代碼都發(fā)送給用戶羊异,這樣攻擊者的代碼就成功的顯示在用戶的頁面上面了。
![](aaa.png)、<a href="javascript:alert('xss');">xss</a>
如果惡意代碼寫上某一個(gè)網(wǎng)站的地址野舶,什么的易迹,惡意付賬什么的,想想后果平道。
防御
請(qǐng)記住一句:不要相信用戶的輸入赴蝇。
XSS防御的總體思路是:對(duì)輸入(和URL參數(shù))進(jìn)行過濾,對(duì)輸出進(jìn)行轉(zhuǎn)義巢掺。
參考鏈接: