一、系統出錯時,應該顯示什麼?
在開發過程中,我們經常會遇到資料庫連線失敗、SQL 執行錯誤、使用者輸入不合法,或是網址中的 ID 不存在等情況。
如果直接把 Java 例外訊息或完整堆疊追蹤顯示給使用者,可能會暴露資料表名稱、程式路徑,甚至其他內部資訊。
因此,系統應該將錯誤分成兩個層次:
二、避免把原始錯誤直接印在頁面上
不建議這樣寫:
catch (Exception e) {
response.getWriter().println(e.getMessage());
}
因為 e.getMessage() 可能包含資料庫或程式內部資訊。
可以改成:
catch (Exception e) {
log("取得病患資料時發生錯誤", e);
request.setAttribute(
"errorMessage",
"目前無法取得資料,請稍後再試。"
);
request.getRequestDispatcher(
"/error.jsp"
).forward(request, response);
}
log() 是 Servlet 提供的記錄方法之一,可以將例外資訊寫入伺服器紀錄。實際環境仍應依照部署設定管理日誌的存取權限、保存期限與敏感資訊。
三、建立統一的錯誤頁面
新增 error.jsp:
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
這個頁面只顯示適合使用者閱讀的訊息,不會把完整的 Java 例外內容放在畫面上。
四、再次確認 SQL 使用 PreparedStatement
在之前的文章中,我們已經使用 PreparedStatement 查詢病患資料。
例如:
String sql = "SELECT id, name FROM patient WHERE id = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, patientId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
// 讀取查詢結果
}
}
}
使用參數綁定可以降低 SQL Injection 風險。不要將使用者輸入直接拼接進 SQL 字串。
但這並不代表只使用 PreparedStatement 就能保證整個系統安全,權限檢查、輸出編碼、輸入驗證與資料庫權限同樣重要。
五、網址中的 ID 也不能直接信任
例如,使用者可以嘗試修改網址:
/patient/detail?id=12
把 12 改成其他數字,可能就會查詢另一筆資料。
因此,除了確認 ID 格式正確之外,還必須確認目前登入者是否有權限查看該筆資料。
這類問題屬於存取控制風險。不能只因為使用者已經登入,就讓所有人讀取或修改所有病患資料。
六、建立基本安全檢查清單
七、今日小結
今天學會了統一錯誤訊息、記錄例外、避免直接洩漏內部資訊,也再次檢查 SQL 與資料存取權限。
醫療資訊系統處理的資料具有高度敏感性,因此安全設計不能只在功能完成後才考慮,而應該從開發初期就納入。
下一篇,我們將把目前完成的功能串起來,透過測試確認系統能否正常運作。
參考資料:Jakarta Servlet Specification。