SLEEP(N) 讓當前查詢線程阻塞 N 秒(支持微秒小數),返回 0(正常結束)或剩餘秒數(被信號中斷)。看似無用,實則是 調試神器、壓測利器、也是盲注攻擊載體。本文全景解析 SLEEP() 正規用法與安全風險。
mysql> SELECT SLEEP(1); +----------+ | SLEEP(1) | +----------+ | 0 | +----------+ 1 row in set (1.00 sec) -- 受信號中斷(如 KILL QUERY)返回剩餘秒數 -- 另一會話:KILL-- 當前會話:SELECT SLEEP(10); → 返回 7.000000 (約剩餘 7 秒)
問題:SELECT * FROM t WHERE id=1 耗時 0.00 秒,SHOW PROCESSLIST 根本抓不到。
解法:注入 SLEEP(2) 強制停留,便於觀察:
SELECT SLEEP(2), * FROM t WHERE id=1; -- 或不改業務碼,用 UNION ALL 包裝(需同列數) SELECT SLEEP(2), id, name FROM t WHERE id=1 UNION ALL SELECT 0, id, name FROM t WHERE id=1 LIMIT 0;
現象:SHOW PROCESSLIST 顯示 Time: 2,State: User sleep,確認語句確實在 Server 端執行。
-- 條件命中 3 行 SELECT SLEEP(1), name FROM animals WHERE name LIKE '%ger'; -- 返回 3 行,總耗時 ≈ 3 秒(每行睡 1 秒) -- 條件 0 行 SELECT SLEEP(1), name FROM animals WHERE name='pig'; -- Empty set (0.00 sec) — SLEEP 未執行!
關鍵結論: SLEEP() 在 輸出行階段 執行,WHERE 過濾後無行則不執行。不可用於驗證「查詢是否發起」,只能驗證「結果集行處理」。
-- 模擬 5 秒慢查詢,驗證 max_execution_time / proxy 讀超時 SELECT /*+ MAX_EXECUTION_TIME(2000) */ SLEEP(5), id FROM large_table LIMIT 1; -- 應在 2 秒被終止,報錯 ERROR 3024 (HY000): Query execution was interrupted
攻擊者構造:
SELECT * FROM users WHERE id = 1 AND IF((SELECT SUBSTRING(password,1,1) FROM users WHERE id=1)='a', SLEEP(5), 0);
防禦鐵律:
SELECT/INSERT/UPDATE/DELETE 業務表,無 FILE/SUPER/PROCESS 權限。SLEEP( / BENCHMARK( 等時間函數特徵。max_execution_time(會話/全局)限制單查詢上限。-- 會話 1 BEGIN; UPDATE accounts SET balance=balance-100 WHERE id=1; SELECT SLEEP(30); -- 模擬長業務處理 COMMIT; -- 會話 2(同一行) UPDATE accounts SET balance=balance+50 WHERE id=1; -- 被阻塞,驗證 innodb_lock_wait_timeout
| 版本 | SLEEP() 精度 | 替代方案 |
|---|---|---|
| 5.6+ | 微秒(如 SLEEP(0.001)) | DO SLEEP(1) 不返回結果集 |
| 8.0 | 同 | SELECT /*+ MAX_EXECUTION_TIME(1000) */ ... 原生超時控制 |
SLEEP() 是把雙刃劍:用得好是調試壓測利器,用不好成注入武器。原則:生產環境業務代碼嚴禁出現 SLEEP();調試後必須清理;防禦層面以參數化查詢為本、超時控制為盾、審計報警為眼。