Amazon CloudFront 預設有支援 signed cookies 的驗證方式,而 Azure CDN 有三個提供商,但是並非都可以支援此功能,其中僅有 Azure CDN from Verizon 才有支援,且定價層也需要選擇進階才有支援,本文就介紹該如何來設定和實做站台來驗證結果。
前言
因為某些因素希望使用 Azure CDN,針對安全性部分會產生驗證的 Token,且希望是在 CDN 這一段就做掉驗證,而不用到後端來源程式才去限制,假設來源是儲存體的話,也僅支援 SAS Token,且會需要將 Token 呈現在網址上面,為了避免使用者只需要將網址給予其它人就可以存取特定的 CDN 檔案或頁面,所以需求上想要透過驗證 Cookies 內的 Token 來加強安全性,雖然放 Cookies 一樣容易被取得,但是對於一般使用者分享上,還是相對安全一點。
實做
建立服務和設定
首先是儲存體和 CDN 的建立,這邊就不特別多說明,大家可以參考官方文件來建立,只有一點是定價層請選擇進階 Verizon,此定價層才有辦法支援 Sigend Cookies。
規則引擎設定
- 開啟進階管理平台
進到建立好的 CDN 節點內,點選左邊進階功能再點選管理來開啟 Verzion 的進階管理平台。
- 設定加密金鑰
輸入加密金鑰和選擇加密的最低版本,點選 Update 來儲存設定,這邊就按照需求來設定吧。
設定的頁面底下也包含產生金鑰和解密的功能,後續程式寫好之後可以很方便的來測試金鑰是否正確。也有針對所有參數的設定和說明,大家可以參考看看。
- 開啟規則引擎設定
點選上方選單開啟 Rules Engine V4.0 頁面來建立新的規則引擎。
- 建立新的規則
點選 +New 並輸入規則名稱
- 設定規則
規則設定上可以透過 UI 介面輸入和設定,這邊我選擇直接匯入之前匯出的設定。<policy> <rules> <rule> <description>Cookie auth token</description> <match.url.url-query-param.wildcard name="auth-token" result="match" value="*" ignore-case="true"> <feature.headers.modify-client-response-header action="set" name="Set-Cookie" value="token=%{arg_auth_token}; path=/"/> </match.url.url-query-param.wildcard> </rule> </rules> </policy>
- 部署規則
設定好規則之後並不會直接生效,需要做部署的操作才會真的生效,這邊可以看到剛剛匯入的設定如果使用 UI 設定會如下面圖片所示。點選 Lock Draft as Policy,之後再點選 Deploy Request,最後選擇部署的環境和輸入原因就可以建立了。Lock 之後無法再編輯規則的,如果還有需要調整的,請確認完再點選 Lock,否則的話需要複製舊有的在建立新的規則。 - 等候部署生效
接下來可以看到底下畫面,再來就是等到正式生效,大約需要等 10 分鐘,一直到狀態顯示成 Deployed 就好了。
實做程式
接下來實做一個簡易的電子書平台來驗證 CDN 和設定。
- 加解密程式
加解密程式直接參考 Verzion 的範例程式,就不另外說明,裡面有各種程式語言的實做範例。 - 設定金鑰
先建立設定金鑰的頁面和程式。請自行將 {Key} 修改成前面步驟設定的金鑰。程式是將產生的加密值寫入到指定的 Cookies。public IActionResult SetToken() { var generator = new ECTokenGenerator(); var key = "{Your Key}"; var expireTime = DateTime.UtcNow.AddMinutes(5); string clientIp = HttpContext.Connection.RemoteIpAddress.ToString(); var token = generator.EncryptV3(key, expireTime, clientIp); Response.SetTokenCookies(token); var decryptdToken = generator.DecryptV3(key, token, false); NameValueCollection qscoll = HttpUtility.ParseQueryString(decryptdToken); TempData["token"] = qscoll; TempData["Message"] = "Set Token OK!"; return View(); }
@if (TempData["token"] != null) { var tokens = TempData["token"] as System.Collections.Specialized.NameValueCollection; <table class="table"> <thead> <tr> <th>參數</th> <th>值</th> </tr> </thead> <tbody> @foreach (var token in tokens) { <tr> <td>@token</td> <td>@tokens[token.ToString()]</td> </tr> } </tbody> </table> }
- 移除金鑰功能
順便提供移除金鑰的功能,單純把 Cookies 清空。public IActionResult RemoveToken() { Response.SetTokenCookies("", true); TempData["Message"] = "Remove Token OK!"; return RedirectToAction("Index"); }
- 電子書頁面
顯示特定的圖片檔案來做為簡易線上電子書功能。public IActionResult eBooks(string page) { return View(); }
@{ var pageNum = Context.Request.Query["Page"].ToString(); var images = $"https://CDNURL/eBooks/{pageNum}.png"; } <nav aria-label="Page navigation example"> <ul class="pagination"> <li class="page-item"><a class="page-link" asp-action="eBooks" asp-route-page="01">01</a></li> <li class="page-item"><a class="page-link" asp-action="eBooks" asp-route-page="02">02</a></li> <li class="page-item"><a class="page-link" asp-action="eBooks" asp-route-page="03">03</a></li> <li class="page-item"><a class="page-link" asp-action="eBooks" asp-route-page="04">04</a></li> <li class="page-item"><a class="page-link" asp-action="eBooks" asp-route-page="05">05</a></li> </ul> </nav> <h1>@pageNum</h1> <p><img src="@images" /></p>
驗證
首先在還沒有設定驗證的 Cookies 的時候圖片是無法顯示的,直接存取也會顯示 403。
接下來設定 Token 的 Cookies。
確認 Cookies 有設定了,再回到電子書頁面,就可以正確的看到圖片了。
需要測試和完整程式碼可以點選以下連結。
結論
在實務上會針對特定使用者在登入之後才有辦法存取某些特定資源,而這些資源可能是靜態檔案,為了伺服器負擔,我們會希望加上 CDN 來分散流量和快取,但是仍然需要保持安全性,避免使用者直接把網址分享就可以讓其它使用者看到,雖然將 Token 放到 Cookies 一樣有被洩漏的可能性,但是我們在產生 Token 的時候可以把時間和 IP 參數以及特定字串都加進去,這樣不僅可以增加安全性,未來真的有洩漏的時候,也可以透過 Token Decode 來找出是哪一個使用者洩漏的,這樣對於一般需求上來說應該是足夠的,如果大家有更好建議也可以在留言中討論。