The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

使用 SPA 單一頁面應用程式設計網頁表單應考量密碼管理器需求

由於我公司有使用玉山商業銀行來處理帳務,我經常需要登入玉山全球智匯網以審核放行一些廠商款項,我一直以來都有使用密碼管理器的習慣,確保我的每個網站所使用的密碼都是不一樣的,強化資訊安全。不過,該網站在我用了數幾年之後,從不久之前開始,該網站完全無法自動登入了,深入研究後才發現,原來他們把網頁上的表單欄位的 id 屬性移除了!這篇文章我來說說我解決此問題的過程,也說說前端工程師應注意的事項!

問題分析

由於很少有網站無法使用「密碼管理器」來管理密碼,玉山全球智匯網應該是我第一個遇到完全無法自動輸入密碼的網站,所以我就分析了一下這個網站的前端結構,以下是我的探索發現:

  1. 我很驚訝的發現,該網站的首頁竟然用了一個我從 2000 年開始就沒在用的標籤 FRAMESET 標籤!😅

    我不太清楚這個設計的用意為何?故意讓網站上的網址不會動嗎?好多銀行都有類似設計,我聽過大多數的原因,都是不希望客戶按下 F5 重新整理網頁,只要按了 F5 重新整理網頁就要登出!但我必須老實說,你其實有更好的選擇,可以不用透過 FRAMESET 來降低網頁的可及性(Accessibility)!

    FRAMESET

  2. 我拿掉了 FRAMESET 的包袱,直接進入 FRAMESET 中的主要頁面,網頁依然可以正常瀏覽!

    他們用了 Angular 4.4.6 版本來建置這個網站,可以理解從建站之初到現在,從來沒有升級過版本,老實說我會擔心使用太舊版本的前端框架或函式庫,會帶來一些資訊安全上的風險。

    Angular 4.4.6

    題外話,網頁上載入的 JavaScript 刻意加上 deferasync 在這裡是完全無意義的,這個位置已經在頁尾了阿!😅

  3. 我的密碼管理器自動填入了「使用者名稱」與「密碼」欄位,但是「顧客ID/代號」這個欄位就沒有自動填入了!

    因為這個網站我已經用了很多年,這個欄位一直是可以填入的,但前幾個月似乎就開始無法自動填入了。

    密碼管理器自動填入了「使用者名稱」與「密碼」欄位,但是「顧客ID/代號」這個欄位就沒有自動填入

  4. 原來「顧客ID/代號」這個欄位缺乏 id 屬性,導致密碼管理器無法定位到該欄位!

    我想這就是主因了,一定是在某次更版的過程中,開發人員將 id 屬性移除了。老實說,在 SPA 架構下,表單欄位的 idname 屬性其實一點都不重要,沒有這兩個屬性一樣可以開發表單互動,所以經常被開發人員忽略。

    在網頁中的標籤使用 id 屬性,除了可以讓傳統 JavaScript 更好的透過 DOM API 定位到該元素(Element)之外,還可以讓 E2E 自動化測試更好的定位到該欄位。很自然的,此舉也可以讓「密碼管理器」更容易找到重要的表單欄位,加以記憶與管理!

    欄位缺乏 id 屬性

解決方案

解決這個問題其實也不難,透過 Tampermonkey 擴充套件,外加我寫的一段 Userscript 就可以在網頁載入時自動加入該欄位的 id 屬性,如此一來「密碼管理器」就可以識別這個欄位了!👍

以下是完整的 Userscript 實作:

// ==UserScript==
// @name         玉山銀行: 添加遺失的表單欄位 id 屬性
// @version      1.0.0
// @description  修復玉山銀行玉山全球智匯網登入頁面無法使用密碼管理器的問題
// @license      MIT
// @homepage     https://blog.miniasp.com/
// @homepageURL  https://blog.miniasp.com/
// @website      https://www.facebook.com/will.fans
// @source       https://github.com/doggy8088/TampermonkeyUserscripts/raw/main/src/ESUN_Add_Field_ID.user.js
// @namespace    https://github.com/doggy8088/TampermonkeyUserscripts/raw/main/src/ESUN_Add_Field_ID.user.js
// @match        https://gib.esunbank.com/*
// @author       Will Huang
// @run-at       document-start
// @icon         https://www.google.com/s2/favicons?sz=64&domain=https://gib.esunbank.com
// ==/UserScript==

(async function () {
    "use strict";
    let it = setInterval(() => {
        let elm = document.querySelector('input[placeholder="顧客ID/代號"],input[placeholder="顾客ID/代号"],input[placeholder="Customer ID/No"]');
        if (elm) {
            elm.id = 'inputCustomerId';
            clearInterval(it);
        }
    }, 60);
})();

後記

其實我可以理解銀行的資安需求永遠站在最高標準看待,我覺得他們應該是站在保護顧客的立場,不希望客戶透過「任何工具」代客戶輸入密碼。不過我老實說,如果真的有這種想法,這個資安觀念真的是有點過時了,就跟要求客戶每半年要改一次密碼,而且不能用過去 5 次用過的密碼一樣過時!

現在上網風險之高,已經跟 20 年前不太一樣了,撞庫攻擊是常年下來一直都非常有效的攻擊手段,因此在不同網站之間共用相同密碼,早就被證明是一種相當危險的密碼管理政策,應該盡可能的避免。若網站要求使用者經常變更密碼,又不允許他們使用工具輸入密碼,這無疑是要人把密碼貼在鍵盤底下,不然就是設定「一二兩排」這種 IT 人常見的「高強度密碼」!😆

這篇文章希望可以讓使用任何 JS 前端框架的開發者知道,適時的加上 id 屬性到欄位上,其實可以減少用戶的麻煩,提高用戶滿意度,更能提升資安強度!👍

喔喔,還有,拜託 <frameset> 不要再用了!事實上在 HTML5 規格中,根本沒有這個標籤,這是一個 W3C 完全棄用的歷史產物,瀏覽器為了讓老舊網頁還可以瀏覽,所以才留著的。網站做這麼棒,花了那麼多力氣在 UI/UX 上面,我覺得用現代化的角度去製作網站(Modern Web),應該是全體 Web 開發者的共識啦,不鼓勵再去使用任何已廢棄的 HTML 標籤或 Web API!🔥

相關連結

留言評論