OpenCart 如何修正 SameSite 問題(以串接ezShip為例)

關於 SameSite Cookie 所帶來的問題,絕大部分有串接外部金物流網站的 OpenCart,應該都無法避免,還不了解 SameSite 問題的,建議先閱讀 Chrome 80 SameSite Cookie 對 OpenCart 網站的影響 這篇文章。

本篇文章要以 ezShip台灣便利配 的串接為例,來說明如何解決 SameSite 所造成的問題,以及解決的方法之一。

ezShip台灣便利配 提供了店到店( 超商取貨/ 超商取貨付款) 配送服務,為首家與OK、萊爾富、全家三大通路合作之店到店( 超商取貨/ 超商取貨付款) 服務平台,當買家從購物平台(如 OpenCart),透過 API 的串接跳轉到 ezShip,選擇要收件的超商門市之後,ezShip 會將買家選取的超商門市相關資料,傳送回購物網站,傳送的 API 相關資料,可參考 http://www.ezship.com.tw/file/ezship_WebOrder_HttpRequest.pdf

由於買家在 ezShip 選好超商門市後,ezShip 是以 post 方式跨站跳轉到購物平台,所以在 SameSite 機制下,將無法同時傳送 cookie 到購物平台,但其實主機端的 Session 值都還是存在的,OpenCart 2.x 的 Session 資料預設是儲存在檔案中,而 OpenCart 3.x 則是改為儲存在資料庫中,你可以檢視資料庫中的 table oc.session,如下圖。

既然系統無法再從跳轉回來的同時取得 cookie 中的 Session ID,那就改以其他方式,讓網站有辦法在 ezShip 跳轉回來時,取得正確的 Session ID 值即可,做法有很多種,在這裡舉一種方式來介紹,相信大家能觸類旁通,在不同的情境下,思考出適合的應變作法。

我們檢視 ezShip 的串接 API 文件,可以發現傳遞的參數有預留一個 webPara 欄位,可以讓你送到 ezShip 再轉送回來,能作為 Token 驗證功能,事實上大部分的串接 API 規格,都有預留這樣的欄位,有的甚至不只一個欄位。

ezShip 的串接 API 文件

我們可以利用這個欄位來傳遞 Session ID,如此一來,在轉回 OpenCart 時,就可以取得原本得 Session ID 值了,為了提高安全性,先對 Session 進行編碼再傳送,等回傳後再進行解碼,應該是更理想,不過要注意編碼之後的字串長度,不能超過 API 的限制,免得無法傳送或被裁剪,造成回傳後無法解碼。

最後要修改的是,在 OpenCart 的 Session Class 中,修改判斷 Session ID 的 Cookie 是否存在的邏輯,在讀不到 Cookie 之後,多做一個動作,判斷是否有 $_POST 的 webPara 參數,若有,就進行解碼,若能解碼成功,就把取出的 Session ID 值,指定給 session_id(),如此,OpenCart 就能得到原本的 Session ID 值,繼續正常運作了。

除了將前述 Session ID 編碼後再傳送,以提高安全性之外,另外還能透過其他的資訊驗證,來增加過濾的關卡,例如跳轉離開前,記錄下使用者的 IP (關聯至 Session ID),等轉回時再比對一次,另外,也能在轉回時,檢查 Referer 網址,判斷是否為剛剛跳出的目的網址,或建立白名單來檢查,都是可以讓網站更安全的設置。

上面的作法,不是唯一的應變方法,畢竟每一種串接的情境都未必相同,但個人認為,透過這種類 Token 的模式來解決 SameSite Cookie 問題,是比直接將 Cookie 的 SameSite 設為 None 來的安全,在你的網站還沒開發出理想的解決方式之前,簡單的將 Cookie 改設為 SameSite=None,是可以作為過渡時期的處置方式,但長久之計,還是應該讓 SameSite 機制生效,過濾掉可能的惡意行為,才是比較理想的應變模式。