顯示具有 security 標籤的文章。 顯示所有文章
顯示具有 security 標籤的文章。 顯示所有文章

2014年5月14日 星期三

使用 OAuth2 refresh token 的好處

初次看到 refresh token, 想說這東西真礙事, 原本 OAuth2 基本的流程已將使用者的密碼和授權分離, 看起來很完美了, 為什麼授權的 access token 之外, 還要多一個 refresh token? 看了 security - Why Does OAuth v2 Have Both Access and Refresh Tokens? 才明白 refresh token 更進一步提升安全性。

access token 解決了:

  • 使用者不需告訴應用程式密碼
  • 使用者可以細分授權項目
  • 使用者可隨時撤消授權

當 access token 外洩的時候, 只要使用者立即撤消授權, 就可以中止傷害, 相較於修改密碼方便許多 (一組密碼可能用在一到多個帳號上, 甚至可能忘了那些帳號使用同一密碼)。

但是為了減少使用者的認證的麻煩, 一個 access token 通常會授權使用很長一段時間 (比方說一個月)。使用者不容易知道什麼時候發出去的 access token 外洩了, 傷害期可能會太長。外洩的原因可能是使用 access token 取存服務時被竊聽破解, 或是在裝置端的應用程式 (PC、smartphone) 存放的 access token 直接外洩。

有沒有辦法可以兼顧減少使用者認證, 又降低 access token 外洩的傷害期呢? 答案是加上 refresh token。設定授權的 access token 在很短的時間內會過期 (如一小時), 然後 client(應用程式) 要用 refresh token + client id + client secret 取得新的 access token。

client 不需要透過使用者就可以取得新的 access token, 不會打擾到使用者。沒有 client secret 的情況下, 單是外洩 access token, 別人用一小段時間後, token 就過期了, 傷害時間很短。

不過要達到上面的好處, client 必須在不同地方存下 refresh token 和 client secret, 偷懶存在同一地方, 一但破台也是一樣。

security - Why Does OAuth v2 Have Both Access and Refresh Tokens? 還有人提供其它回答, 提到 refresh token 有機會減輕認證伺服器的負擔, 不知是意外的副作用, 或是設計之初就有考慮到。愈了解 OAuth2, 愈覺得制定 OAuth2 的人們考慮的真詳細, 提供很彈性的機制讓實作者可自行調整安全的程度、使用者操作的便利性以及伺服器負擔。

Btw, Google 提供 refresh token 的方式還有一點小撇步。使用預設的方式認證時, Google 只有第一次認證時才會傳回 refresh token, 之後重新認證不會再傳回 refresh token。除非使用者到 Google Account 的 Security 分頁取消對 app 的授權, 接著再認證才會再收到 refresh token。或是在認證 URL 的參數加上 approval_prompt=force。詳細的內容寫在 Using OAuth 2.0 for Web Server Applications - Google Accounts Authentication and Authorization — Google Developers:

Important: When your application receives a refresh token, it is important to store that refresh token for future use. If your application loses the refresh token, it will have to re-prompt the user for consent before obtaining another refresh token. If you need to re-prompt the user for consent, include the approval_prompt parameter in the authorization code request, and set the value to force.

2010年11月8日 星期一

2010 OWASP Top 10 網站潛在漏洞摘要

這類知識看過沒持續用, 沒多久就會忘光, 來寫個簡單摘要協助記憶。官網有完整的說明和例子, 這裡簡單地用我自己的方式說一次:

  1. Injection: 攻擊者利用 GET / POST 在參數裡塞入部份程式, 取得不被允許的資料。如常見的 SQL Injection。解法就是別相信使用者傳進來的東西, 限制傳入東西的型別和可能的文字模式。
  2. XSS: 誤執行攻擊者傳來的程式。比方說 textarea 就容易混入這類程式。別傻傻的直接引入使用者傳上來的 html, 最好只充許純文字, 或限制 html tag 的功能。
  3. Broken authentication and session management: 如網站傻傻的將 session id 放到 URL 上, 讓使用者有機會不小心外洩 session id。
  4. Insecure direct object reference: 沒有在存取資料時再多做權限檢查。攻擊者在登入後, 可以傳特定參數取得他不能取得的資料。比方說 alice 登入後在需要傳 user id 的地方改傳 bob 卻能取得 bob 的資料。
  5. CSRF: B 網站偷塞會連向 A 網站更新資料的操作 (如塞在 img src 裡), 若使用者登入 A 網站後沒登出, 接著連到 B 網站就中招了。解法是 A 網站要確保任何會更新資料的操作, 在產生輸入的頁面時先塞一段密文, 在更新資料時檢查是否有這段密文, 確保使用者真的從 A 網站送出請求。
  6. Security misconfiguration: 網站留一堆後門給攻擊者用, 像是可以由外連上管理者介面 (如 phpMyAdmin), 或是可以瀏覽網站目錄, 下載 source code 之類的。還有用別人的 framework / lib, 明明說有 security patch 卻不更新。
  7. Insecure cryptographic storage: 加密的 key 和加密的資料存在一起, 有防和沒防一樣。password 檔沒加 salt (之前寫過的文章: 為啥要用 salt)。
  8. Failure to restrict URL access: 沒有在載入頁面時檢查權限。比方說只有查使用者是否有登入, 並隱藏使用者沒權限執行的頁面。一但攻擊者知道那些 URL, 就能登入後直接連上去使用。
  9. Insufficient Transport Layer Protection: 輸入密碼或重要資料 (如卡號) 時沒透過加密連線, 攻擊者可透過監聽網路封包的方式取得這些資料。或是一般使用的情況取得 session id。另外有用 SSL 但沒設好 certificate, 造成惡意攻擊網站有機會偽裝成自家網站, 騙取輸入資料。
  10. Unvalidated Redirects and Forwards: 隨便在參數提供 redirect 不小心被當作攻擊的打手, 使用者沒注意看就點了別人貼 A 站的網址, 卻被 redirect 到 B 站去。或是自己的站被用來導到其它頁面, 卻沒有在進入頁面時檢查權限, 於是 redirect 被當作任意門使用, 能進入不被允許的頁面。
上面的問題的防範方式有幾個共通點:

  • 使用者都是來亂的 (咦?), 別相信他們輸入的資料。
  • 所有操作都要在最後一層確實把關, 前面把關都只是做表面工夫, 真正要避免問題, 要在最後一關守好。
  • 別圖個方便亂開洞給別人攻擊。

Btw, 好的 web framework 會幫忙處理一些事, 像 Django 1.2 就強迫使用防範 CSRF 的機制, 除非使用者自己耍蠢硬用 GET 更新資料, 才會中招。使用者密碼也有用 salt, 減少開發者犯錯的機會。

2010年9月25日 星期六

django 自行新增使用者的作法以及為何密碼檔裡要加 salt

寫測試程式時需要先建個測試帳號, 為了確保任何人取得原始碼後能直接跑成功測試, 我在測試碼執行時檢查是否已建立測試帳號, 若沒有則自己建一個。

單純地新增 User 放入 username 和 password 是行不通的, 順著命令 createsuperuser 看原始碼, 發覺正確的寫法如下:
try:
    user = models.User.objects.get(username='test')
except models.User.DoesNotExist, e:
    print 'Test account doest not exist. Now create it.'
    user = models.User(username='test', is_active=True)
    user.set_password('testtest')
    user.save()

好奇之下看了一下 set_password 怎麼寫的 (./contrib/auth/models.py):
def set_password(self, raw_password):
    import random
    algo = 'sha1'
    salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
    hsh = get_hexdigest(algo, salt, raw_password)
    self.password = '%s$%s$%s' % (algo, salt, hsh)

可以看到 Django Auth 用 sha1 和 salt 產生加密後的字串。於是查了一下 salt 的功效, 這回才真的明白它的用處。

Wikipedia 上舉了幾個情境:
  • 在沒用 salt, 且用單字當密碼的情況, 一但密文被取得後, 攻擊者只要查事先建好的表即可馬上找出原始密碼。表的 key 是 sha1 加密的結果, value 是原碼。
  • 同上, 改用 Rainbow table 建一般性的表, 而不是單純用字典檔。題外話, Rainbow table 的概念滿有趣的, 用到機率和時間空間取捨的技巧。
所以, 若加密時有用 salt, 攻擊者得另外取得 salt 才能開始攻擊。即使 salt 也存在密文裡, 攻擊者也得照「規矩」來試密碼, 不能用事先建好的表。
若整份密碼檔用同一份 salt, 攻擊者可以在取得 salt 後開始建表, 之後整份密文就可以用同一份表查出結果。但是當每個使用者的 salt 都不同時, 攻擊者只好每個密文都全試一次, 無法建表加速破解。換句話說, 破解單一特定使用者密碼的時間一樣, 但用多份 salt 讓破解所有人密碼的時間大增。
當然, 若使用者的密碼較長, 或是有摻雜攻擊者建表時沒用到的字元 (如攻擊者只用字母和數字, 使用者的密碼有含符號), 那加不加 salt 也沒差。不過任何當過系統管理員的人都知道, 絕大多數使用者的密碼都是很簡單的..., 簡單地加個 salt 可以降低不少風險, 讓攻擊者要花更多時間取得明文, 發覺密碼檔外洩時, 可以減少損失。

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...