關于Token,你應該知道的十件事(轉載)

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

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

  1. Token 應該被保存起來(放到 local / session stograge 或者 cookies)
    在單頁應用程序中,有些用戶刷新瀏覽器后會帶來一些跟 token 相關的問題。而解決方法很簡單:你應該把 token 保存到起來:放到 session storage, local storage 或者是客戶端的 cookie 里。而瀏覽器不支持 session storage 時都應該轉存到 cookies 里。
    如果你想“我把 token 保存到 cookie ,不就跟以前沒有任何分別?”??墒窃谶@種情況下你只是把 cookie 當作一個儲存機制,而不是一種驗證機制。(比如說,這個 cookie 不會被 Web 框架用于用戶驗證,所以沒有 XSRF 攻擊的危險)。
  2. Tokens 除了像 cookie 一樣有有效期,而且你可以有更多的操作方法
    Tokens 應該有一個有效期(在 JSON Web Tokens 中是作為 exp
    屬性),否則其他人只要登錄過一次就可以永遠地通過 API 的驗證。Cookies 基于同樣的理由也有一個有效期。
    在 Cookies 的使用中,有不同的選項可以控制 cookie 的生命周期:
  3. cookies 可以在瀏覽器關閉后刪除(session cookies);2. 另外你可以實現服務器端的檢查(通常由你使用的 Web 框架完成),還有也可以實現絕對有效期或彈性有效期(sliding window expiration);3. Cookies 可以帶有有效期地保存起來(瀏覽器關閉后也不刪除)。

而在 tokens 的使用中,一旦 token 過期,只需要重新獲取一個。你可以使用一個接口去刷新 token:

  1. 讓舊的 token 失效;2. 檢查這個用戶是不是還存在,權限是否被取消或者任何對你的程序來說是有必要的;3. 得到一個更新了有效期的 token。

你甚至可以把 token 原來的發布時間也保存起來,并且強制在兩星期后重新登錄什么的。

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(當 token 的生存期比較長的時候這很有必要)那么你需要一個 token 的生成管理器去作檢查。

  1. Local / session storage 不會跨域工作,請使用一個標記 cookie
    如果你設置一個 cookie 的域名為 .yourdomain.com
    它將可以被 youdomain.com
    和 app.yourdomain.com
    獲取,這樣用戶登錄并且轉到app.yourdomain.com
    后也能很容易地從主域名找回這個 cookie(假如你的是電商網站)。
    而另一方面,保存在 local / session storage 的 tokens,就不能從不同的域名中讀?。ㄉ踔潦亲佑蛎膊恍校D悄隳茉趺醋??
    一個可能的選擇是,當用戶通過 app.yourdomain.com
    上面的驗證時你生成一個 token 并且作為一個 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
中你可以檢查這個 cookie 是不是已經存在了,并且如果存在的話就轉到 app.youromdain.com
去。從這以后,這個 token 將會對程序的子域名以及之后通常的流程都有效(直到這個 token 超過有效期)。
不過這將會導致 cookie 存在但 token 被刪除了或其他意外情況的發生。在這種情況下,用戶將不得不重新登錄。但重要的是,像我們之前說的,我們不會這個用 cookie 作為驗證方法,只是作為一個存儲機制去支持存儲信息在不同的域名中。

  1. 每個 CORS(跨域資源共享)請求都會帶上預請求(Preflight request)
    有些人指出 Authorization header 不是一個simple header,因此對于一個特定的 URLs 的所有請求都會帶上一個預請求。

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 ....

但這只會發生在你發送 Content-Type: application/json
時。不過這說明已經出現在絕大多數的程序中了。
一個小小的警告,the OPTIONS
請求不會帶有 Authorization header 自身,所以你的網絡框架應該支持區別對待 OPTISON
和后來的請求。(微軟的 IIS 因為某些原因好像會有問題)。

  1. 當你需要流傳送某些東西,請用 token 去獲取一個已簽名的請求。
    當使用 cookies 時,你可以很容易開始一個文件的下載或流傳送內容。然而,在 tokens 的使用中,請求是通過 XHR 完成的,你不能依賴于它。而解決方法應該是像 AWS 那樣通過生成一個簽名了的請求,例如,Hawk Bewits 是一個很好的框架去啟用它:
    Request:

12

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

Response:

1

ticket=lahdoiasdhoiwdowijaksjdoaisdjoasidja

這個 ticket 是無狀態并且是基于 URL 的:host + path + query + headers + timestamp + HMAC,并且有一個有效期。所以它可以用于像只能在5分鐘內去下載一個文件。
你然后可以轉到 /download-file/123? ticket=lahdoiasdhoiwdowijaksjdoaisdjoasidja
中去。服務器就會檢查這個 ticket 是不是有效然后像正常一樣開始下一步的服務。

  1. XSSXSRF 要更容易防范
    XSS 攻擊的原理是,攻擊者插入一段可執行的 JavaScripts 腳本,該腳本會讀出用戶瀏覽器的 cookies 并將它傳輸給攻擊者,攻擊者得到用戶的 Cookies 后,即可冒充用戶。但是要防范 XSS 也很簡單,在寫入 cookies 時,將 HttpOnly
    設置為 true
    ,客戶端 JavaScripts 就無法讀取該 cookies 的值,就可以有效防范 XSS 攻擊。因為 Tokens 也是儲存在本地的 session storage 或者是客戶端的 cookies 中,也是會受到 XSS 攻擊。所以在使用 tokens 的時候,必須要考慮過期機制,不然攻擊者就可以永久持有受害用戶帳號。
    相比 XSS,XSRF 的危害性更大,因為大多數 Web 框架都已經內置了 XSS 防范機制(例如在 Ruby on Rails 中,用戶的輸入在輸出的時候都會做轉義
    操作,攻擊者插入的腳本就無法執行),對于大部分開發者而言,甚至連 XSRF 都不知道是什么玩意,更別提防范了。XSRF 目前并不是每個 Web 框架都有防范機制,因此開發者更應該留意 XSRF 。
  2. 注意 token 的大小
    Token 機制在每次請求 API 的時候,都需要帶上一個 Authorization
    的 Http Header 。

123

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

123

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

Token 的大小其實由你儲存在 token 中的信息量所決定,例如可能有 nickname
,openid
等開發者另外加上的信息。
但是 session cookies 機制只需要一個字串作為用戶標識即可(例如 PHP 的 PHPSESSIONID),其中關于用戶的信息都會直接儲存到服務端的數據庫中,當用戶請求時才從數據庫中撈出來用。
當然 Token 機制也可以仿照 session cookies 機制這么做了,也是個有效控制 token 大小的方法。
Token 中只保留關鍵的幾條身份標識信息,其余都放到數據庫里面了,權限控制的時候再撈出。這樣做的好處是,開發者可以完全掌控 token,因為關鍵信息都已經是你代碼和數據庫中的一部分了,想怎么弄都可以了。
舉個例子:

123

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

12345678910

app.use('/api', // 首先檢查 token; expressJwt({secret: secret}), // 然后再從數據庫中撈出用戶信息。 function(req, res, next) { req.user.extra_data = get_from_db(); next(); });

另外值得一提的是,你也可以把東西都丟 Cookies 里面(而不是只丟個身份標識字串)。只要確保資料經過了嚴格的加密,攻擊者無法利用,現在有些 Web 框架已經有類似機制,例如 Nodejs 的這個插件 mozilla/node-client-sessions。

  1. 有需要的話,要加密并且簽名 token
    雖然 TLS/SSL 機制可以隔絕大多數中間人攻擊,但是如果 token 中帶有了用戶的敏感信息,開發者也應該要加密這些信息。
    使用 JWT(文中第 9 點) 可以加密 token,但是由于目前大多數 Web 框架還未支持 JWT,所以可以使用 AES-CBC 算法加密 token。

1234567891011121314151617181920

app.post('/authenticate', function (req, res) { // 校驗用戶; // 加密 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 (加密后簽名)做法。

當然你也可以用文中的第 7 點,直接將敏感信息丟數據庫中。

  1. 將 JSON Web Tokens 應用到 OAuth 2
    OAuth 2 是一個解決身份驗證的授權協議,并且廣泛地使用了 token 。
    用戶通過 OAuth 2 協議授權第三方應用權限,然后服務器返回一個 access_token
    給第三方應用,通常也帶有 scope
    參數,第三方應用通過帶上access_token
    請求服務器,可以在授權范圍(scope)內調用 API。
    一般來說,類似這種 token 是不透明的,就是核心數據都儲存以 hash-table 結果儲存在服務器中,客戶端只持有一個令牌
    (access_token),任何人都可以用這個令牌在授權范圍(scope)內調用服務器端的 API。
    Signed tokens(例如 JWT))和這種形式的 token 最主要的區別是,JWT 是無狀態的,它不儲存在服務端 hash-table 中,服務端中不保留 JWT 請求的相關信息,JWT 會把授權信息和 API 調用返回都丟一起返回給客戶端。
    JWT 通常以 Base64 + AES 方式編碼傳輸。OAuth 2 協議也支持 JWT,因為 OAuth 2 并未限制 access_token 數據格式,你可以將 JWT 應用在 OAuth 2 上。
  2. Tokens 不是萬能的解決方法,得根據你的需求自行采用
    這些年來,我們幫助過不少大公司實現了他們的以 Token 為基礎的驗證授權架構。曾經有一家 10k + 員工,有著大量數據的公司,他們想實現一個中央權限管理系統,其中有一個需要是某個員工只能讀取某個國家某個醫院某個床位的id
    和name
    字段數據,想想這樣的細粒度的權限管理是多么難實現,無論是技術上還是行政上。
    當然采用 tokens 與否,得看大家的具體需求,但是,要忠告大家的是,不要什么內容都寫到 tokens 了,加之前想想有沒有這個必要。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容