網站安全 SHA-256 密碼保護 Cloudflare Access 小白指南

網站密碼保護全攻略:從明文到 Hash 到後端驗證

Thomas 的 15 個網站從「一看源代碼就破」升級到「SHA-256 Hash + 強密碼」。完整安全等級對比 + 小白實操指南。

目錄
01 為什麼你的網站需要保護? 02 安全等級金字塔 03 關鍵詞彙中英文對照 04 Hash 是什麼?(圖解) 05 前端 vs 後端的根本區別 06 密碼強度對比 07 Thomas 的最終方案(實操步驟) 08 Thomas 的實際配置總結 FAQ 常見問題

第一章:為什麼你的網站需要保護?

Thomas 有 15 個 Cloudflare Pages 網站,部署在不同的 .pages.dev 域名下。其中大約 5 個包含私人數據:

問題很簡單:這些網址只要被人知道,任何人打開就能看到全部內容。沒有任何登入、密碼、或驗證機制。

比喻

想像你的日記本放在圖書館的開放書架上。書架沒上鎖,也沒寫「私人勿碰」。只要有人路過隨手翻開,你的秘密就全曝光了。網站不加保護就是這個狀態。

第二章:安全等級金字塔

不同的保護方式差距巨大。以下從低到高排列,分數越高越安全:

Level 5 · 95+ 分
企業級方案
多因素認證(MFA)、IP 白名單、WAF 規則。小網站不需要。
Level 4 · 90 分
Cloudflare Access + Google OAuth
點 Google 登入按鈕自動驗證。需 GCP 設定 OAuth Client(壞了就進不去,Thomas 踩過這個坑)。依賴 Google 服務,設定複雜。
Level 3 · 85 分
Cloudflare Access Email OTP
Cloudflare 後端攔截,沒驗證的人連 HTML 都拿不到。輸入 email → 收驗證碼 → 輸入 → 進入。只允許指定 email。無法用 DevTools 繞過。缺點:每次要開信箱找驗證碼。
Level 2 · 50-60 分 ← Thomas 現在
SHA-256 Hash 密碼
HTML 裡寫 if(sha256(input) === '7e3f630...')。源代碼只看到一串亂碼,看不到密碼。防住了 90% 的普通人。懂技術的人可以用 DevTools 繞過顯示邏輯,但暴力破解 13 位英數密碼要幾百年。
Level 1 · 15 分
明文密碼
HTML 裡寫 if(input === '19890117')。右鍵 → 查看源代碼 → 搜索 password → 看到密碼。破解時間:10 秒
Level 0 · 0 分
無保護
任何人打開網址就能看到內容。適合:公開資訊(教會崇拜流程、講道索引)。
Thomas 之前的狀態

在升級之前,Thomas 的私人網站是 Level 1(明文密碼)。密碼直接寫在 JavaScript 裡,任何會按右鍵的人 10 秒就能破解。

升級後的狀態

現在是 Level 2(SHA-256 Hash)。源代碼裡只看到一串 64 字元的亂碼,看不到密碼。配合 13 位英數密碼,暴力破解需要幾百年。

第三章:關鍵詞彙中英文對照

英文 中文 白話解釋
Password 密碼 你設的通關暗號
Plaintext 明文 密碼直接寫在代碼裡,任何人都看得到
Hash 雜湊 / 哈希 把密碼變成一串亂碼,不可逆(不能從亂碼算回密碼)
SHA-256 安全雜湊演算法 一種 Hash 算法,輸出 64 個字元的十六進制字串
Brute Force 暴力破解 一個一個試所有可能的密碼組合
Frontend 前端 瀏覽器裡跑的代碼(用戶看得到全部)
Backend 後端 伺服器上跑的代碼(用戶看不到)
DevTools 開發者工具 瀏覽器按 F12 打開的調試面板
OTP 一次性密碼 One-Time Password,用一次就作廢的驗證碼
OAuth 開放授權 用 Google / Facebook 帳號登入第三方網站的協議
localStorage 本地儲存 瀏覽器存數據的地方(記住你 30 天不用重新輸密碼)
Cloudflare Access 零信任門禁 Cloudflare 的後端保護服務,攔在網站前面
Token 令牌 驗證通過後拿到的「通行證」
Refresh Token 續期令牌 通行證過期時自動續期用的

第四章:Hash 是什麼?(圖解)

比喻

Hash 就像把一顆雞蛋打進碗裡攪成蛋液。你看著蛋液不可能還原成完整的雞蛋,但每次用同一顆雞蛋打出來的蛋液「模樣」是一樣的。密碼的 Hash 就是這個原理。

加密過程

密碼 → SHA-256 → Hash 值
密碼 "thomas2026hub"
↓ SHA-256 演算法(單向,不可逆)
"7e3f630357ccb84ce792687f853d028d08ef280b2958ae6ee8e4a7054eb0b5e8"
這串亂碼存在 HTML 源代碼裡

驗證過程

用戶輸入密碼 → 即時計算 → 比對
用戶在密碼框輸入密碼
↓ 瀏覽器即時算 SHA-256
得到 Hash 值
↓ 和存的 Hash 比對
一樣 → 通過!解鎖內容
不一樣 → 拒絕!顯示錯誤
Hash 的三個關鍵特性

第五章:前端 vs 後端的根本區別

這是理解網站安全最重要的一個概念。前端方案(Level 1-2)和後端方案(Level 3-4)的區別不是「好不好」,而是「內容在哪裡」。

前端驗證(Level 1-2)
用戶打開網頁
瀏覽器下載完整 HTML(包含所有內容 + 密碼邏輯)
密碼框擋住畫面(但內容已經在瀏覽器裡了)
懂技術的人用 DevTools 直接看到隱藏的內容
後端驗證(Level 3-4)
用戶打開網頁
Cloudflare 攔截:「你是誰?」
驗證通過前,HTML 根本不發送到瀏覽器
DevTools 打開也只看到 Cloudflare 的登入頁
驗證通過後,Cloudflare 才把真正的 HTML 傳過去
為什麼前端方案最多 60 分

因為用戶已經拿到了所有東西。密碼框只是一個「遮罩」,內容全在瀏覽器裡。就像把金庫大門敞開,只在門口拉了一條警戒線 -- 守規矩的人不會跨過去,但想進的人抬腳就過了。

那為什麼 Thomas 還是選 Level 2?

因為 Level 3-4 的設定複雜度高、依賴外部服務(Cloudflare Access / Google OAuth),每次打開都要驗證很煩。而 Level 2 對 Thomas 的場景已經夠用:私人數據不是國家機密,只需要擋住隨便點進來的陌生人。安全要匹配場景,不需要用大砲打蚊子。

第六章:密碼強度對比

密碼 類型 可能組合數 暴力破解時間 等級
19890117 8 位純數字 1 億 幾秒 ~ 幾分鐘 極弱
password 8 位英文小寫 2,000 億 幾小時
Thomas88 8 位英數混合 2 兆 幾天 中等
thomas2026hub 13 位英數混合 1023 幾百年
T#hom@s_2026! 13 位含特殊字元 1026 幾萬年 很強
Thomas 的選擇

Thomas 選了 thomas2026hub(13 位英數混合),暴力破解需要幾百年。對個人網站來說綽綽有餘。密碼好記(名字 + 年份 + 用途),不需要特殊字元也足夠強。

第七章:Thomas 的最終方案(實操步驟)

方案:SHA-256 Hash + 強密碼 + localStorage 30 天記住

1 選一個強密碼

13 位以上,英文加數字混合。不需要特殊字元,但不要用純數字或常見詞。

例如:thomas2026hubmysite2026passcloudpage2026x

2 生成 SHA-256 Hash

方法 A:用 Python(推薦)

python -c "import hashlib; print(hashlib.sha256('你的密碼'.encode()).hexdigest())"

方法 B:用瀏覽器 DevTools Console

按 F12 打開 DevTools → 切到 Console 頁籤 → 貼上:

crypto.subtle.digest('SHA-256', new TextEncoder().encode('你的密碼'))
  .then(h => console.log(
    Array.from(new Uint8Array(h))
      .map(b => b.toString(16).padStart(2,'0'))
      .join('')
  ))

把輸出的那串 64 字元十六進制字串複製下來。

3 在 HTML 裡加入密碼保護代碼

把以下代碼加入你的 HTML 文件。PW_HASH 換成你在 Step 2 得到的 Hash 值。

<!-- 密碼畫面 -->
<div id="pw-screen">
  <input type="password" id="pw-input"
         placeholder="請輸入密碼" />
  <button onclick="checkPw()">進入</button>
  <div id="pw-error" style="display:none">
    密碼錯誤
  </div>
</div>

<!-- 主要內容(預設隱藏) -->
<div id="main-content" style="display:none">
  ...你的網頁內容...
</div>

<script>
const PW_HASH = '你的hash值';
const PW_KEY  = location.hostname + '_pw';
const PW_DAYS = 30;

async function sha256(s) {
  const d = new TextEncoder().encode(s);
  const h = await crypto.subtle.digest('SHA-256', d);
  return Array.from(new Uint8Array(h))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
}

function isPwOk() {
  try {
    const d = JSON.parse(
      localStorage.getItem(PW_KEY) || '{}'
    );
    return d.ok && Date.now() < d.exp;
  } catch (e) { return false; }
}

async function checkPw() {
  const hash = await sha256(
    document.getElementById('pw-input').value
  );
  if (hash === PW_HASH) {
    localStorage.setItem(PW_KEY, JSON.stringify({
      ok: 1,
      exp: Date.now() + PW_DAYS * 864e5
    }));
    document.getElementById('pw-screen')
      .style.display = 'none';
    document.getElementById('main-content')
      .style.display = 'block';
  } else {
    document.getElementById('pw-error')
      .style.display = 'block';
    document.getElementById('pw-input').value = '';
  }
}

// 自動檢查:30天內免重輸
if (isPwOk()) {
  document.getElementById('pw-screen')
    .style.display = 'none';
  document.getElementById('main-content')
    .style.display = 'block';
}

// Enter 鍵觸發
document.getElementById('pw-input')
  .addEventListener('keydown', function(e) {
    if (e.key === 'Enter') checkPw();
  });
</script>

4 部署到 Cloudflare Pages

cd 你的網站文件夾
npx wrangler pages deploy . --project-name=你的項目名 --branch=main

部署完成後,打開網站確認密碼框正常顯示、輸入密碼後能正常進入。

5 把密碼存入 GCP Secret Manager(防忘記)

echo -n "你的密碼" | gcloud secrets create 網站名-password \
  --project=你的專案 --data-file=-

這樣即使忘記密碼,也能用 gcloud secrets versions access latest --secret=網站名-password 查回來。

第八章:Thomas 的實際配置總結

網站保護狀態一覽

網站 保護方式 密碼 Hash 安全等級
thomas-hub.pages.dev SHA-256 Hash 7e3f63... Level 2(60 分)
thomas-ai-notes.pages.dev SHA-256 Hash 7e3f63... Level 2(60 分)
bch-fy2025.pages.dev SHA-256 Hash 7e3f63... Level 2(60 分)
triathlon-2026.pages.dev SHA-256 Hash 7e3f63... Level 2(60 分)
triathlon-2026-v2.pages.dev SHA-256 Hash 7e3f63... Level 2(60 分)
passion-worship-2026.pages.dev Level 0(公開)
nvda-dashboard.pages.dev Level 0(公開)

密碼存放位置:GCP Secret Manager cloudflare-pages-password-thomastangnz(project: nvda-strategy

所有受保護網站使用同一組密碼,方便記憶。Hash 值統一為 7e3f630357ccb84ce...

FAQ:常見問題

Q1:SHA-256 Hash 可以被反向破解嗎?

理論上不行。SHA-256 是單向函數,從 Hash 值無法算回原始密碼。但如果密碼太簡單(如 1234),攻擊者可以用「彩虹表」(預先算好的常見密碼 Hash 對照表)直接查到。所以密碼長度和複雜度很重要。

Q2:有人用 DevTools 繞過前端密碼怎麼辦?

如果真的擔心,升級到 Level 3(Cloudflare Access Email OTP)。對 Thomas 的場景來說,能繞過前端密碼的人本來就不是他的目標防護對象。Level 2 的目的是擋住 90% 隨便點進來的人。

Q3:為什麼不直接用 Cloudflare Access?

三個原因:(1) 設定複雜,特別是 Google OAuth 模式需要 GCP 配置,壞了就進不去;(2) 每次打開都要驗證(Email OTP 要去信箱找驗證碼);(3) 靜態網站不值得這麼折騰,SHA-256 Hash 夠用了。

Q4:localStorage 被清掉了怎麼辦?

重新輸入密碼即可。localStorage 只是「記住登入狀態 30 天」的便利功能,不影響安全性。清除瀏覽器數據、換瀏覽器、換電腦,都需要重新輸入密碼。

Q5:所有網站用同一個密碼安全嗎?

對 Thomas 的場景足夠安全。這些都是個人靜態網站,不涉及金融交易或帳號系統。用同一個密碼的好處是只需要記一組,存一份到 GCP Secret Manager 就不怕忘記。

Q6:以後想升級到 Level 3 難嗎?

不難。在 Cloudflare Zero Trust Dashboard 建立 Application,設定 Email OTP Policy,指定允許的 email 即可。不需要改 HTML 代碼。可以參考 GCP OAuth + 網站認證踩坑指南 裡的相關說明。

為什麼保護 安全金字塔 詞彙表 Hash 圖解 前端vs後端 密碼強度 實操步驟 配置總結 ← 返回首頁 ↑ 回到頂部