Skip to content

Security: chenmitchell/opay-invoice-skill

Security

SECURITY.md

SECURITY.md — 安全政策

本專案為非官方的個人專案。本文件是我個人整理的安全實務建議,不構成任何法規符合性聲明,也不取代歐付寶官方的安全規範或你所屬組織的資安政策。


目錄


1. 金鑰處理原則

歐付寶電子發票的敏感憑證有三個:MerchantID、HashKey、HashIV。 其中 HashKey 與 HashIV 是 AES-128 的金鑰與 IV,拿到這兩個就能偽造與解密所有請求。

唯一可接受的存放位置

位置 可否 說明
.env 檔(且已加入 .gitignore) ✅ 本機開發的標準做法
Secret Manager / Vault ✅ 正式環境建議做法(AWS Secrets Manager、GCP Secret Manager、HashiCorp Vault 等)
CI/CD 的加密 secrets ✅ GitHub Actions Secrets 等
容器編排的 secret 物件 ✅ Kubernetes Secret(搭配加密的 etcd)
環境變數(由上述來源注入) ✅ 程式只從 os.environ 讀
程式碼中的常數 ❌ 一旦 commit 就永久留在 git 歷史
前端 JavaScript / HTML / CSS ❌ 等同公開;官方文件明文禁止
設定檔且已 commit ❌ config.json、application.yml 一樣不行
log 輸出 ❌ log 常被集中收集、長期保存、多人可讀
錯誤訊息 / stack trace ❌ 可能直接回傳給使用者
測試程式碼、fixture ❌ 測試碼一樣會進 git
Slack / Email / 工單系統 ❌ 這些系統的保存期限與存取控制通常比你想的寬鬆

基本做法

# .gitignore 必須包含
.env
.env.*
!.env.example
*.pem
*.key
secrets/
# ✅ 正確
import os
hash_key = os.environ["OPAY_HASH_KEY"]

# ❌ 錯誤
hash_key = "ejCk326UnaZWKisg"   # 就算是測試值,也不要寫死在程式邏輯裡

關於測試環境金鑰

本 repo 的文件與 .env.example 中出現的以下數值,是歐付寶官方技術文件公開列出的測試環境值:

用途 MerchantID HashKey HashIV
B2C 一般特店(僅測試環境) 2000132 ejCk326UnaZWKisg q9jcZX8Ib9LM8wYk
離線發票特店(僅測試環境) 2045501 9XWzRmj7UJESChyn sriQzbe1llJqk67P

它們只能用在 einvoice-stage.opay.tw。 正式環境的憑證必須向歐付寶申請,且永遠不得出現在任何 repo 中。

⚠️ 測試環境也會產生真實的發票紀錄、消耗真實的字軌號碼,且只能作廢不能刪除。不要把測試環境當沙箱亂打。


2. 絕對不要做的事

# 禁止事項 為什麼
1 把 HashKey / HashIV commit 進任何 git repo git 歷史難以徹底清除,即使 force push 也可能殘留於 fork、CI 快取、他人本機
2 把金鑰放進前端 JS / HTML / CSS 任何人按 F12 就看得到;官方文件明文禁止
3 在正式環境用 Issue 做健康檢查或連線測試 會產生真發票、消耗字軌號碼,只能作廢不能刪除
4 對開立/作廢/折讓/註銷重開盲目重試 逾時不等於沒開立,重送可能開出兩張發票
5 把正式金鑰放在測試環境或 staging 環境隔離失效,測試資料會污染正式帳務
6 在 log 中輸出完整的 Data 明文 明文含買受人 Email、手機、統編等個資
7 用同一組金鑰跑多個不相關的系統 一處外洩即全面外洩,且無法定位來源
8 把發票 PDF/截圖未脫敏就放進 repo 或工單 含發票號碼、統編、買受人資訊
9 讓 AI agent 自動對正式環境送出不可逆請求 開立/作廢/折讓皆不可復原
10 用 verify=False 或忽略 TLS 憑證驗證 中間人攻擊可直接取得你的 Data 與回應

3. 不得把真實資料貼進 AI 對話

Warning

不要把真實發票資料或買受人個資貼進任何 AI 對話。 這包含 Claude、ChatGPT、Gemini、Copilot、Cursor、以及任何雲端 AI 服務。

不得貼入的內容

類別 具體項目
憑證 正式環境 MerchantID、HashKey、HashIV、廠商後台帳密
買受人個資 Email、手機號碼、姓名、地址、統一編號、載具號碼
發票資料 真實發票號碼、隨機碼、金額明細、品項內容
內部資訊 內部系統網址、資料庫連線字串、內部訂單編號規則
原始封包 未脫敏的請求/回應 JSON、完整的加密 Data 字串

為什麼

  • 多數 AI 服務的對話可能被保存、可能被用於服務改善(依方案與設定而定)。
  • 對話記錄可能被同組織其他成員存取。
  • 個資外洩涉及《個人資料保護法》責任,責任在你不在 AI 廠商。
  • 加密後的 Data 配上金鑰即可還原——貼了金鑰又貼封包等於直接公開明文。

正確做法:先脫敏再問

- 我的請求是 {"MerchantID":"3012345","HashKey":"aB3xK9pQ2mN7vL4z", ...}
- 開立失敗,發票號碼 AB12345678,買受人 wang@example.com,統編 12345678
+ 我的請求結構是 {"MerchantID":"<REDACTED>","HashKey":"<REDACTED>", ...}
+ 開立失敗,發票號碼 AA00000000,買受人 user@example.com,統編 00000000
+ 錯誤是 TransCode=0,訊息「時間戳記錯誤」

脫敏對照表:

真實資料 貼給 AI 時用
正式 MerchantID <MERCHANT_ID> 或 2000132(公開測試值)
HashKey / HashIV <REDACTED>
發票號碼 AA00000000
統一編號 00000000
Email user@example.com
手機 0900000000
手機條碼載具 /ABCD123
愛心碼 001
訂單編號 ORDER-0001

錯誤訊息、欄位名稱、程式碼結構、狀態碼都可以貼——AI 需要的是這些,不是你的真實資料。


4. 金鑰外洩的處置流程

一旦懷疑 HashKey / HashIV 外洩(誤 commit、貼錯視窗、離職交接、第三方服務被入侵),立即依序執行:

第 1 步:輪換金鑰(立即,不要等調查結束)

  1. 登入歐付寶廠商後台:
  2. 依後台指示重新產生 HashKey / HashIV。
  3. 若後台無法自助輪換,立即聯繫歐付寶客服/業務窗口,說明是資安事件並要求緊急處理。

舊金鑰在新金鑰生效前仍然有效。時間就是損害範圍,不要為了「先查清楚」而延後輪換。

第 2 步:更新所有使用端

  • Secret Manager / .env / CI secrets / 容器 secret 全部更新。
  • 逐一重啟服務並確認新金鑰生效(跑一次唯讀 API,例如 GetInvoiceWordSetting)。
  • 檢查有沒有遺漏的環境(排程主機、備援機、開發者本機)。

第 3 步:清除外洩來源

# 如果是誤 commit:先確認範圍
git log --all -S 'HashKey' --oneline

# 從歷史移除(會改寫歷史,需協調所有協作者重新 clone)
# 建議使用 git-filter-repo 或 BFG Repo-Cleaner

⚠️ 改寫 git 歷史不代表金鑰安全了。 只要曾經 push 到遠端(尤其是公開 repo),就必須假設它已經外洩——輪換是唯一的補救,清歷史只是善後。

第 4 步:檢查有無異常使用

  • 到廠商後台查詢近期發票開立紀錄,確認有無非預期的開立、作廢、折讓。
  • 比對自家稽核記錄與後台紀錄,找出差異。
  • 檢查字軌剩餘量是否有異常消耗。

第 5 步:記錄與檢討

  • 記錄事件時間軸、影響範圍、處置動作。
  • 依你所屬組織的規範判斷是否需要通報。
  • 補上防呆:pre-commit hook、secret scanning、CI 檢查。

預防:加上 pre-commit 檢查

# .git/hooks/pre-commit(記得 chmod +x)
#!/bin/sh
if git diff --cached | grep -nE 'Hash(Key|IV)\s*[=:]\s*["\x27][A-Za-z0-9]{16}["\x27]'; then
  echo "❌ 偵測到疑似金鑰,已阻擋 commit。確認無誤請用 --no-verify。"
  exit 1
fi

也建議在 GitHub repo 設定中啟用 Secret scanning 與 Push protection。


5. 傳輸與環境安全

項目 要求
TLS 僅支援 TLS 1.2 以上,僅開放 443 port。不得停用憑證驗證。
防火牆 以 FQDN 設定 einvoice.opay.tw、einvoice-stage.opay.tw——官方主機 IP 不固定,鎖 IP 會在對方換機時全面失效。
主機時間 RqHeader.Timestamp 驗證區間只有 10 分鐘,主機必須跑 NTP。
環境隔離 測試與正式的金鑰、資料庫、log 完全分離。正式金鑰不得出現在非正式環境。
回傳網址 不支援中文網址,請用 punycode。
出站代理 若經由代理出網,確認代理不會記錄請求 body(Data 欄位是加密的,但仍屬敏感)。
相依套件 定期更新加密相關套件(pycryptodome、OpenSSL)。

6. 稽核與可追溯性

發票是帳務憑證,不可逆操作必須留下可追溯的記錄。

必須記錄的操作

  • 開立(Issue / DelayIssue / OfflineIssue)
  • 作廢(Invalid / OfflineInvalid / AllowanceInvalid)
  • 折讓(Allowance / AllowanceByCollegiate)
  • 註銷重開(VoidWithReIssue)
  • 字軌設定變更(AddInvoiceWordSetting / UpdateInvoiceWordStatus)

每筆記錄應包含

欄位 說明
時間戳記 含時區
操作者 使用者帳號或系統識別(不要只寫 system)
操作類型 開立/作廢/折讓/…
業務識別 RelateNumber、發票號碼
請求摘要 不含金鑰、不含完整個資,只留必要欄位
回應結果 TransCode、RtnCode、RtnMsg
冪等鍵 用於逾時後比對

稽核 log 本身的安全

  • 存取權限最小化,不要放進全公司可讀的日誌平台。
  • 個資欄位(Email、手機、統編)遮蔽後再寫入。
  • 保存期限依你所屬組織與相關法規規定辦理(發票相關憑證的法定保存年限請諮詢會計師或稅務專業人員,本文件不提供法律意見)。
  • 不可修改(append-only)或有變更記錄。

7. 漏洞回報

適用範圍

本政策涵蓋本 repo 的內容:程式碼模板、文件中的安全建議、測試工具。

不涵蓋歐付寶的服務本身。若你發現的是歐付寶平台的漏洞,請直接聯繫歐付寶官方,不要在本 repo 公開。

怎麼回報

Important

請勿開公開 issue 回報安全問題。 公開 issue 等於在修復前先告訴所有人怎麼利用它。

請走以下私下管道(擇一):

  1. GitHub Security Advisory(建議):在本 repo 的 Security → Report a vulnerability 提交私密回報。
  2. 透過維護者網站的聯絡方式:https://www.mitch.tw

請提供

  • 問題描述與影響範圍
  • 重現步驟(請用測試環境與脫敏資料)
  • 受影響的檔案與版本
  • 你認為的修復方向(若有)

我的處理

階段 目標時間
確認收到 7 個工作天內
初步評估與回覆 14 個工作天內
修復並發布 視嚴重程度,高風險優先

本專案由我一個人在下班時間維護,無法承諾 SLA。以上是努力目標,不是保證。 我不提供漏洞獎金,但會在 CHANGELOG.md 致謝(除非你希望匿名)。

已知且不視為漏洞的項目

  • 文件中出現官方公開的測試環境金鑰(2000132 / ejCk326UnaZWKisg / q9jcZX8Ib9LM8wYk、2045501 / 9XWzRmj7UJESChyn / sriQzbe1llJqk67P)——這些是官方技術文件明列的公開值,僅適用測試環境。
  • 模板程式碼未實作某項防護,但文件中已明確標示「這是模板,正式使用前請自行補強」。

8. 本專案自身的安全承諾

  • ✅ 本 repo 不含任何正式環境金鑰,也不接受含正式金鑰的 PR。
  • ✅ 本 repo 不收集任何使用者資料,沒有 telemetry、沒有追蹤、沒有外部呼叫(除了你自己主動發出的 API 請求)。
  • ✅ 模板程式碼的相依套件刻意最小化:Node.js 驗證器零相依、PHP client 只用內建擴充。
  • ✅ 所有 .env.example 中的金鑰皆為官方公開測試值,且逐處標註「僅測試環境」。
  • ✅ 測試主控台的「開立測試發票」預設關閉,需同時設定環境變數與前端勾選確認才會送出。

9. 上線前安全檢查清單

金鑰
[ ] .env 已在 .gitignore 中,且 git status 看不到它
[ ] git log -S 'HashKey' 查不到任何真實金鑰
[ ] 正式金鑰存在 Secret Manager,不在程式碼、不在設定檔
[ ] 前端 bundle 中搜尋 HashKey / HashIV 無結果
[ ] 已啟用 GitHub secret scanning 與 push protection

環境
[ ] 測試與正式環境的金鑰、資料庫、log 完全分離
[ ] 主機 NTP 正常,時間偏移 < 1 分鐘
[ ] 防火牆以 FQDN 設定,非鎖 IP
[ ] TLS 憑證驗證未被停用

程式
[ ] 兩層回應碼(TransCode 與 RtnCode)都有檢查
[ ] 開立/作廢/折讓/註銷重開沒有自動重試
[ ] 逾時處理是「先用 GetIssue 查詢」而非直接重送
[ ] 每筆不可逆操作都有冪等鍵與稽核記錄
[ ] log 中沒有完整的 Data 明文、沒有金鑰、個資已遮蔽
[ ] 正式環境沒有任何以 Issue 作為健康檢查的排程

流程
[ ] 有金鑰輪換的操作手冊與負責人
[ ] 有字軌餘量告警與負責人
[ ] 團隊都知道「不可把真實資料貼進 AI 對話」

本文件為個人整理的實務建議,不構成法律或資安顧問意見,也不宣稱符合任何標準或法規。 正式環境的安全要求請以歐付寶官方規範、你所屬組織的資安政策,以及專業人員的意見為準。

There aren't any published security advisories