NLS_DATE_FORMAT 參數控制資料庫日期時間的顯示與隱含轉換格式。正確設定可避免日期格式不匹配錯誤,但不當配置更可能引發嚴重的 SQL 注入風險。
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
適用於開發測試、特定報表查詢,不影響其他用戶。
Windows: 註冊表 HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOME0 新增字串值 NLS_DATE_FORMAT = YYYY-MM-DD HH24:MI:SS
Linux/Unix: export NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'(寫入 .bash_profile 或 /etc/profile 永久生效)
⚠️ 注意:部分 Shell/平台需同時設定 NLS_LANG=AMERICAN_AMERICA.ZHS16GBK(或對應字元集)才能生效。
可透過 ALTER SYSTEM SET NLS_DATE_FORMAT=... SCOPE=SPFILE 修改,但需重啟實例且影響全庫,極易造成應用程式相容性問題,生產環境慎用。
風險根源: Oracle 允許 NLS_DATE_FORMAT 設定為任意字串,甚至包含 SQL 片段。當應用程式使用字串拼接而非綁定變數執行日期查詢時,惡意設定的格式字串可導致 SQL 注入。
-- 惡意設定
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD"; DROP TABLE users; --';
-- 應用程式拼接 SQL(極度危險)
sql := "SELECT * FROM orders WHERE order_date = '" || user_input_date || "'";
-- 實際執行變為:
SELECT * FROM orders WHERE order_date = '2024-01-01"; DROP TABLE users; --';
即使欄位型別為 DATE,隱含轉換 TO_DATE(:input, NLS_DATE_FORMAT) 仍會觸發。
-- JDBC PreparedStatement
String sql = "SELECT * FROM orders WHERE order_date = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setDate(1, java.sql.Date.valueOf("2024-01-01"));
// PL/SQL 綁定變數
DECLARE
v_date DATE := DATE '2024-01-01';
BEGIN
FOR r IN (SELECT * FROM orders WHERE order_date = v_date) LOOP
-- process
END LOOP;
END;