關(guān)于Token胞此,你應(yīng)該知道的十件事(轉(zhuǎn)載)

轉(zhuǎn)自 http://alvinzhu.me/blog/2014/08/26/10-things-you-should-know-about-tokens/

原文是一篇很好的講述 Token 在 Web 應(yīng)用中使用的文章,而這是我和 Special 合作翻譯的譯文涩金。

  1. Token 應(yīng)該被保存起來(lái)(放到 local / session stograge 或者 cookies)
    在單頁(yè)應(yīng)用程序中谱醇,有些用戶刷新瀏覽器后會(huì)帶來(lái)一些跟 token 相關(guān)的問(wèn)題。而解決方法很簡(jiǎn)單:你應(yīng)該把 token 保存到起來(lái):放到 session storage, local storage 或者是客戶端的 cookie 里步做。而瀏覽器不支持 session storage 時(shí)都應(yīng)該轉(zhuǎn)存到 cookies 里副渴。
    如果你想“我把 token 保存到 cookie ,不就跟以前沒有任何分別全度?”煮剧。可是在這種情況下你只是把 cookie 當(dāng)作一個(gè)儲(chǔ)存機(jī)制,而不是一種驗(yàn)證機(jī)制勉盅。(比如說(shuō)佑颇,這個(gè) cookie 不會(huì)被 Web 框架用于用戶驗(yàn)證,所以沒有 XSRF 攻擊的危險(xiǎn))草娜。
  2. Tokens 除了像 cookie 一樣有有效期挑胸,而且你可以有更多的操作方法
    Tokens 應(yīng)該有一個(gè)有效期(在 JSON Web Tokens 中是作為 exp
    屬性),否則其他人只要登錄過(guò)一次就可以永遠(yuǎn)地通過(guò) API 的驗(yàn)證宰闰。Cookies 基于同樣的理由也有一個(gè)有效期茬贵。
    在 Cookies 的使用中,有不同的選項(xiàng)可以控制 cookie 的生命周期:
  3. cookies 可以在瀏覽器關(guān)閉后刪除(session cookies)移袍;2. 另外你可以實(shí)現(xiàn)服務(wù)器端的檢查(通常由你使用的 Web 框架完成)解藻,還有也可以實(shí)現(xiàn)絕對(duì)有效期或彈性有效期(sliding window expiration);3. Cookies 可以帶有有效期地保存起來(lái)(瀏覽器關(guān)閉后也不刪除)葡盗。

而在 tokens 的使用中舆逃,一旦 token 過(guò)期,只需要重新獲取一個(gè)戳粒。你可以使用一個(gè)接口去刷新 token:

  1. 讓舊的 token 失效路狮;2. 檢查這個(gè)用戶是不是還存在,權(quán)限是否被取消或者任何對(duì)你的程序來(lái)說(shuō)是有必要的蔚约;3. 得到一個(gè)更新了有效期的 token奄妨。

你甚至可以把 token 原來(lái)的發(fā)布時(shí)間也保存起來(lái),并且強(qiáng)制在兩星期后重新登錄什么的苹祟。

12345678910111213141516

app.post('/refresh_token', function (req, res) { // verify the existing token var profile = jwt.verify(req.body.token, secret); // if more than 14 days old, force login if (profile.original_iat - new Date() > 14) { // iat == issued at return res.send(401); // re-logging } // check if the user still exists or if authorization hasn't been revoked if (!valid) return res.send(401); // re-logging // issue a new token var refreshed_token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 }); res.json({ token: refreshed_token });});

如果你需要撤回 tokens(當(dāng) token 的生存期比較長(zhǎng)的時(shí)候這很有必要)那么你需要一個(gè) token 的生成管理器去作檢查砸抛。

  1. Local / session storage 不會(huì)跨域工作,請(qǐng)使用一個(gè)標(biāo)記 cookie
    如果你設(shè)置一個(gè) cookie 的域名為 .yourdomain.com
    它將可以被 youdomain.com
    和 app.yourdomain.com
    獲取树枫,這樣用戶登錄并且轉(zhuǎn)到app.yourdomain.com
    后也能很容易地從主域名找回這個(gè) cookie(假如你的是電商網(wǎng)站)。
    而另一方面砂轻,保存在 local / session storage 的 tokens,就不能從不同的域名中讀瘸埂(甚至是子域名也不行)庄呈。那你能怎么做?
    一個(gè)可能的選擇是斜纪,當(dāng)用戶通過(guò) app.yourdomain.com
    上面的驗(yàn)證時(shí)你生成一個(gè) token 并且作為一個(gè) cookie 保存到 .yourdomain.com

1234567

$.post('/authenticate, function() { // store token on local/session storage or cookie .... // create a cookie signaling that user is logged in $.cookie('loggedin', profile.name, '.yourdomain.com');});

然后,在 youromdain.com
中你可以檢查這個(gè) cookie 是不是已經(jīng)存在了盒刚,并且如果存在的話就轉(zhuǎn)到 app.youromdain.com
去。從這以后,這個(gè) token 將會(huì)對(duì)程序的子域名以及之后通常的流程都有效(直到這個(gè) token 超過(guò)有效期)贮聂。
不過(guò)這將會(huì)導(dǎo)致 cookie 存在但 token 被刪除了或其他意外情況的發(fā)生寨辩。在這種情況下,用戶將不得不重新登錄靡狞。但重要的是甸怕,像我們之前說(shuō)的,我們不會(huì)這個(gè)用 cookie 作為驗(yàn)證方法梢杭,只是作為一個(gè)存儲(chǔ)機(jī)制去支持存儲(chǔ)信息在不同的域名中。

  1. 每個(gè) CORS(跨域資源共享)請(qǐng)求都會(huì)帶上預(yù)請(qǐng)求(Preflight request)
    有些人指出 Authorization header 不是一個(gè)simple header武契,因此對(duì)于一個(gè)特定的 URLs 的所有請(qǐng)求都會(huì)帶上一個(gè)預(yù)請(qǐng)求。

12345678910

OPTIONS https://api.foo.com/barGET https://api.foo.com/bar Authorization: Bearer ....OPTIONS https://api.foo.com/bar2GET https://api.foo.com/bar2 Authorization: Bearer ....GET https://api.foo.com/bar Authorization: Bearer ....

但這只會(huì)發(fā)生在你發(fā)送 Content-Type: application/json
時(shí)届垫。不過(guò)這說(shuō)明已經(jīng)出現(xiàn)在絕大多數(shù)的程序中了全释。
一個(gè)小小的警告装处,the OPTIONS
請(qǐng)求不會(huì)帶有 Authorization header 自身浸船,所以你的網(wǎng)絡(luò)框架應(yīng)該支持區(qū)別對(duì)待 OPTISON
和后來(lái)的請(qǐng)求。(微軟的 IIS 因?yàn)槟承┰蚝孟駮?huì)有問(wèn)題)糟袁。

  1. 當(dāng)你需要流傳送某些東西,請(qǐng)用 token 去獲取一個(gè)已簽名的請(qǐng)求形帮。
    當(dāng)使用 cookies 時(shí),你可以很容易開始一個(gè)文件的下載或流傳送內(nèi)容辩撑。然而,在 tokens 的使用中合冀,請(qǐng)求是通過(guò) XHR 完成的君躺,你不能依賴于它。而解決方法應(yīng)該是像 AWS 那樣通過(guò)生成一個(gè)簽名了的請(qǐng)求林螃,例如俺泣,Hawk Bewits 是一個(gè)很好的框架去啟用它:
    Request:

12

POST /download-file/123Authorization: Bearer...

Response:

1

ticket=lahdoiasdhoiwdowijaksjdoaisdjoasidja

這個(gè) ticket 是無(wú)狀態(tài)并且是基于 URL 的:host + path + query + headers + timestamp + HMAC,并且有一個(gè)有效期横漏。所以它可以用于像只能在5分鐘內(nèi)去下載一個(gè)文件熟掂。
你然后可以轉(zhuǎn)到 /download-file/123? ticket=lahdoiasdhoiwdowijaksjdoaisdjoasidja
中去。服務(wù)器就會(huì)檢查這個(gè) ticket 是不是有效然后像正常一樣開始下一步的服務(wù)华畏。

  1. XSSXSRF 要更容易防范
    XSS 攻擊的原理是尊蚁,攻擊者插入一段可執(zhí)行的 JavaScripts 腳本,該腳本會(huì)讀出用戶瀏覽器的 cookies 并將它傳輸給攻擊者横朋,攻擊者得到用戶的 Cookies 后,即可冒充用戶琴锭。但是要防范 XSS 也很簡(jiǎn)單晰甚,在寫入 cookies 時(shí),將 HttpOnly
    設(shè)置為 true
    决帖,客戶端 JavaScripts 就無(wú)法讀取該 cookies 的值,就可以有效防范 XSS 攻擊地回。因?yàn)?Tokens 也是儲(chǔ)存在本地的 session storage 或者是客戶端的 cookies 中俊鱼,也是會(huì)受到 XSS 攻擊。所以在使用 tokens 的時(shí)候畅买,必須要考慮過(guò)期機(jī)制并闲,不然攻擊者就可以永久持有受害用戶帳號(hào)谷羞。
    相比 XSS帝火,XSRF 的危害性更大,因?yàn)榇蠖鄶?shù) Web 框架都已經(jīng)內(nèi)置了 XSS 防范機(jī)制(例如在 Ruby on Rails 中湃缎,用戶的輸入在輸出的時(shí)候都會(huì)做轉(zhuǎn)義
    操作犀填,攻擊者插入的腳本就無(wú)法執(zhí)行),對(duì)于大部分開發(fā)者而言雁歌,甚至連 XSRF 都不知道是什么玩意知残,更別提防范了靠瞎。XSRF 目前并不是每個(gè) Web 框架都有防范機(jī)制乏盐,因此開發(fā)者更應(yīng)該留意 XSRF 。
  2. 注意 token 的大小
    Token 機(jī)制在每次請(qǐng)求 API 的時(shí)候,都需要帶上一個(gè) Authorization
    的 Http Header 何吝。

123

TokenGET /fooAuthorization: Bearer ...2kb token...

123

CookieGET /fooconnect.sid: ...20 bytes cookie...

Token 的大小其實(shí)由你儲(chǔ)存在 token 中的信息量所決定鹃唯,例如可能有 nickname
爱榕,openid
等開發(fā)者另外加上的信息坡慌。
但是 session cookies 機(jī)制只需要一個(gè)字串作為用戶標(biāo)識(shí)即可(例如 PHP 的 PHPSESSIONID)洪橘,其中關(guān)于用戶的信息都會(huì)直接儲(chǔ)存到服務(wù)端的數(shù)據(jù)庫(kù)中跪者,當(dāng)用戶請(qǐng)求時(shí)才從數(shù)據(jù)庫(kù)中撈出來(lái)用。
當(dāng)然 Token 機(jī)制也可以仿照 session cookies 機(jī)制這么做了熄求,也是個(gè)有效控制 token 大小的方法渣玲。
Token 中只保留關(guān)鍵的幾條身份標(biāo)識(shí)信息,其余都放到數(shù)據(jù)庫(kù)里面了弟晚,權(quán)限控制的時(shí)候再撈出柜蜈。這樣做的好處是仗谆,開發(fā)者可以完全掌控 token,因?yàn)殛P(guān)鍵信息都已經(jīng)是你代碼和數(shù)據(jù)庫(kù)中的一部分了淑履,想怎么弄都可以了隶垮。
舉個(gè)例子:

123

GET /fooAuthorization: Bearer ……500 bytes token….Then on the server:

12345678910

app.use('/api', // 首先檢查 token; expressJwt({secret: secret}), // 然后再?gòu)臄?shù)據(jù)庫(kù)中撈出用戶信息秘噪。 function(req, res, next) { req.user.extra_data = get_from_db(); next(); });

另外值得一提的是狸吞,你也可以把東西都丟 Cookies 里面(而不是只丟個(gè)身份標(biāo)識(shí)字串)。只要確保資料經(jīng)過(guò)了嚴(yán)格的加密指煎,攻擊者無(wú)法利用蹋偏,現(xiàn)在有些 Web 框架已經(jīng)有類似機(jī)制,例如 Nodejs 的這個(gè)插件 mozilla/node-client-sessions至壤。

  1. 有需要的話威始,要加密并且簽名 token
    雖然 TLS/SSL 機(jī)制可以隔絕大多數(shù)中間人攻擊,但是如果 token 中帶有了用戶的敏感信息像街,開發(fā)者也應(yīng)該要加密這些信息黎棠。
    使用 JWT(文中第 9 點(diǎn)) 可以加密 token,但是由于目前大多數(shù) Web 框架還未支持 JWT镰绎,所以可以使用 AES-CBC 算法加密 token脓斩。

1234567891011121314151617181920

app.post('/authenticate', function (req, res) { // 校驗(yàn)用戶; // 加密 token畴栖; var encrypted = { token: encryptAesSha256('shhhh', JSON.stringify(profile)) }; // 給加密后的 token 簽名随静; var token = jwt.sign(encrypted, secret, { expiresInMinutes: 60*5 }); res.json({ token: token });}function encryptAesSha256 (password, textToEncrypt) { var cipher = crypto.createCipher('aes-256-cbc', password); var crypted = cipher.update(textToEncrypt, 'utf8', 'hex'); crypted += cipher.final('hex'); return crypted;}// 上面就是 encrypt-then-MAC (加密后簽名)做法。

當(dāng)然你也可以用文中的第 7 點(diǎn)吗讶,直接將敏感信息丟數(shù)據(jù)庫(kù)中燎猛。

  1. 將 JSON Web Tokens 應(yīng)用到 OAuth 2
    OAuth 2 是一個(gè)解決身份驗(yàn)證的授權(quán)協(xié)議,并且廣泛地使用了 token 照皆。
    用戶通過(guò) OAuth 2 協(xié)議授權(quán)第三方應(yīng)用權(quán)限重绷,然后服務(wù)器返回一個(gè) access_token
    給第三方應(yīng)用,通常也帶有 scope
    參數(shù)纵寝,第三方應(yīng)用通過(guò)帶上access_token
    請(qǐng)求服務(wù)器论寨,可以在授權(quán)范圍(scope)內(nèi)調(diào)用 API。
    一般來(lái)說(shuō)爽茴,類似這種 token 是不透明的葬凳,就是核心數(shù)據(jù)都儲(chǔ)存以 hash-table 結(jié)果儲(chǔ)存在服務(wù)器中,客戶端只持有一個(gè)令牌
    (access_token)室奏,任何人都可以用這個(gè)令牌在授權(quán)范圍(scope)內(nèi)調(diào)用服務(wù)器端的 API火焰。
    Signed tokens(例如 JWT))和這種形式的 token 最主要的區(qū)別是,JWT 是無(wú)狀態(tài)的胧沫,它不儲(chǔ)存在服務(wù)端 hash-table 中昌简,服務(wù)端中不保留 JWT 請(qǐng)求的相關(guān)信息占业,JWT 會(huì)把授權(quán)信息和 API 調(diào)用返回都丟一起返回給客戶端。
    JWT 通常以 Base64 + AES 方式編碼傳輸纯赎。OAuth 2 協(xié)議也支持 JWT谦疾,因?yàn)?OAuth 2 并未限制 access_token 數(shù)據(jù)格式,你可以將 JWT 應(yīng)用在 OAuth 2 上犬金。
  2. Tokens 不是萬(wàn)能的解決方法念恍,得根據(jù)你的需求自行采用
    這些年來(lái),我們幫助過(guò)不少大公司實(shí)現(xiàn)了他們的以 Token 為基礎(chǔ)的驗(yàn)證授權(quán)架構(gòu)晚顷。曾經(jīng)有一家 10k + 員工峰伙,有著大量數(shù)據(jù)的公司,他們想實(shí)現(xiàn)一個(gè)中央權(quán)限管理系統(tǒng)该默,其中有一個(gè)需要是某個(gè)員工只能讀取某個(gè)國(guó)家某個(gè)醫(yī)院某個(gè)床位的id
    和name
    字段數(shù)據(jù)瞳氓,想想這樣的細(xì)粒度的權(quán)限管理是多么難實(shí)現(xiàn),無(wú)論是技術(shù)上還是行政上栓袖。
    當(dāng)然采用 tokens 與否匣摘,得看大家的具體需求,但是叽赊,要忠告大家的是恋沃,不要什么內(nèi)容都寫到 tokens 了必搞,加之前想想有沒有這個(gè)必要必指。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市恕洲,隨后出現(xiàn)的幾起案子塔橡,更是在濱河造成了極大的恐慌,老刑警劉巖霜第,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葛家,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡泌类,警方通過(guò)查閱死者的電腦和手機(jī)癞谒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)刃榨,“玉大人弹砚,你說(shuō)我怎么就攤上這事∈嘞#” “怎么了桌吃?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)苞轿。 經(jīng)常有香客問(wèn)我茅诱,道長(zhǎng)逗物,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任瑟俭,我火速辦了婚禮翎卓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摆寄。我一直安慰自己莲祸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布椭迎。 她就那樣靜靜地躺著锐帜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪畜号。 梳的紋絲不亂的頭發(fā)上缴阎,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音简软,去河邊找鬼蛮拔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛痹升,可吹牛的內(nèi)容都是我干的建炫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼疼蛾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼肛跌!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起察郁,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤衍慎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后皮钠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稳捆,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年麦轰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乔夯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡款侵,死狀恐怖末荐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情喳坠,我是刑警寧澤鞠评,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站壕鹉,受9級(jí)特大地震影響剃幌,放射性物質(zhì)發(fā)生泄漏聋涨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一负乡、第九天 我趴在偏房一處隱蔽的房頂上張望牍白。 院中可真熱鬧,春花似錦抖棘、人聲如沸茂腥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)最岗。三九已至,卻和暖如春朝捆,著一層夾襖步出監(jiān)牢的瞬間般渡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工芙盘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驯用,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓儒老,卻偏偏與公主長(zhǎng)得像蝴乔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子驮樊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容