iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0
Security

醫療數據的資安挑戰與創新解決方案系列 第 11

HTTPS 與憑證簡介(加密傳輸測試)

  • 分享至 

  • xImage
  •  

今天的學習目標是使用 HTTPS 保護醫療資料在傳輸過程中的安全性,了解 SSL/TLS 的基本概念,並用 Python 模擬加密傳輸測試。

一、理論重點

  1. HTTPS(HyperText Transfer Protocol Secure)
    在 HTTP 上加上一層 SSL/TLS 加密,確保資料在傳輸過程中不被竊聽或篡改。

  2. SSL/TLS 憑證
    用來驗證伺服器身份,確保用戶端與真正的伺服器通信,而非中間人攻擊。

  3. 自簽名憑證(Self-signed Certificate)
    開發或測試環境常用,但不被瀏覽器信任;正式環境需使用 CA 簽發的憑證。

  4. Python HTTPS 測試
    可以用 requests 或 httpx 搭配 SSL 憑證進行安全連線測試。

二、案例分享

2021 年,一家醫院推出線上病歷查詢系統,讓病人可以透過網頁查詢自己的檢驗結果與病歷紀錄。系統剛上線時,傳輸仍使用 HTTP,所有病人資料都是明文傳送,沒有任何加密保護。資安團隊在例行檢測時發現,如果有人在同一個公共網路環境中截取封包,可能取得病人的姓名、身分證號、檢驗結果等敏感資訊,存在高度資安風險。

為了防止資料外洩,醫院立刻啟用 HTTPS,並在伺服器安裝 SSL/TLS 憑證,確保病人的每次請求與伺服器的通訊都經過加密。之後,病人再透過網頁登入系統,所有病歷資料在傳輸過程中都被加密,即使被攔截,也無法解讀內容。此舉成功避免了敏感資訊外洩,並提升病人的使用信任度,同時也符合 HIPAA 等醫療資安規範。

三、簡單程式示範

python

# generate_cert.py
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa
import datetime

# 生成私鑰
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
with open("key.pem", "wb") as f:
    f.write(key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption()
    ))

# 生成自簽名憑證
subject = issuer = x509.Name([
    x509.NameAttribute(NameOID.COUNTRY_NAME, "TW"),
    x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Taipei"),
    x509.NameAttribute(NameOID.LOCALITY_NAME, "Taipei"),
    x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Org"),
    x509.NameAttribute(NameOID.COMMON_NAME, "localhost"),
])

cert = x509.CertificateBuilder().subject_name(subject)\
    .issuer_name(issuer)\
    .public_key(key.public_key())\
    .serial_number(x509.random_serial_number())\
    .not_valid_before(datetime.datetime.utcnow())\
    .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))\
    .add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)\
    .sign(key, hashes.SHA256())

with open("cert.pem", "wb") as f:
    f.write(cert.public_bytes(serialization.Encoding.PEM))

print("key.pem 和 cert.pem 已生成完成!")

server.py

from fastapi import FastAPI, Request, HTTPException
import mysql.connector

app = FastAPI()

# 簡單 Token
TOKENS = {
    "patient123": "patient",
    "doctor123": "doctor"
}

# MySQL 連線設定
DB_CONFIG = {
    "host": "localhost",
    "user": "root",
    "password": "Joy940819",
    "database": "health"
}

# 初始化資料表
def init_db():
    conn = mysql.connector.connect(**DB_CONFIG)
    cur = conn.cursor()
    cur.execute("""
    CREATE TABLE IF NOT EXISTS blood_pressure (
        id INT AUTO_INCREMENT PRIMARY KEY,
        systolic INT,
        diastolic INT,
        pulse INT
    )
    """)
    conn.commit()
    conn.close()

init_db()

# 病人上傳 API
@app.post("/upload")
async def upload(request: Request):
    token = request.headers.get("Authorization")
    if TOKENS.get(token) != "patient":
        raise HTTPException(status_code=401, detail="Unauthorized")

    data = await request.json()
    conn = mysql.connector.connect(**DB_CONFIG)
    cur = conn.cursor()
    cur.execute("INSERT INTO blood_pressure (systolic, diastolic, pulse) VALUES (%s, %s, %s)",
                (data["systolic"], data["diastolic"], data["pulse"]))
    conn.commit()
    conn.close()
    return {"status": "success", "data": data}

# 醫師查詢 API
@app.get("/records")
def records(request: Request):
    token = request.headers.get("Authorization")
    if TOKENS.get(token) != "doctor":
        raise HTTPException(status_code=401, detail="Unauthorized")

    conn = mysql.connector.connect(**DB_CONFIG)
    cur = conn.cursor()
    cur.execute("SELECT * FROM blood_pressure ORDER BY id DESC LIMIT 5")
    rows = cur.fetchall()
    conn.close()
    return {"records": rows}

client.py

import requests

url = "https://127.0.0.1:8443/patient_data"
data = {
    "name": "Alice",
    "age": 30,
    "blood_pressure": "120/80"
}

# 用 verify=False 忽略自簽名憑證警告
headers = {"Authorization": "patient123"}
response = requests.post(url, json=data, headers=headers, verify=False)
print(response.status_code, response.json())

先用 Python 快速生成憑證,再來啟動HTTPS

uvicorn server:app --host 127.0.0.1 --port 8443 --reload --ssl-keyfile key.pem --ssl-certfile cert.pem

這時FastAPI 伺服器已經順利啟動,並且使用 HTTPS(8443 埠) 運行中。

假設我是病人上傳資料(POST)

import requests

url = "https://127.0.0.1:8443/upload"
headers = {"Authorization": "patient123"}
data = {"systolic": 120, "diastolic": 80, "pulse": 70}

response = requests.post(url, json=data, headers=headers, verify=False)
print(response.status_code, response.json())

而醫師查詢我(病人)所上傳的資料

url = "https://127.0.0.1:8443/records"
headers = {"Authorization": "doctor123"}

response = requests.get(url, headers=headers, verify=False)
print(response.status_code, response.json())

執行結果如下
https://ithelp.ithome.com.tw/upload/images/20250917/20169331qW3ydUEA49.png


上一篇
醫療數據 API:Token 驗證 + 資料庫整合
下一篇
HTTPS 在醫療情境的真實應用與傳輸驗證
系列文
醫療數據的資安挑戰與創新解決方案13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言