# SQL注入
有使用 SQL 語句操作數據庫的經驗朋友,應該都知道使用 SQL 過程中有一個安全問題叫 SQL 注入。所謂 SQL 注入,就是通過把 SQL 命令插入到 Web 表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的 SQL 命令。為了防止 SQL 注入,在生產環境中使用 Openresty 的時候就要注意添加防范代碼。
延續之前的 ngx_postgres 調用代碼的使用,
~~~
local sql_normal = [[select id, name from user where name=']] .. ngx.var.arg_name .. [[' and password=']] .. ngx.var.arg_password .. [[' limit 1;]]
local res = ngx.location.capture('/postgres',
{ args = {sql = sql } }
)
local body = json.decode(res.body)
if (table.getn(res) > 0) {
return res[1];
}
return nil;
~~~
假設我們在用戶登錄使用上 SQL 語句查詢賬號是否賬號密碼正確,用戶可以通過 GET 方式請求并發送登錄信息比如:
~~~
http://localhost/login?name=person&password=12345
~~~
那么我們上面的代碼通過 ngx.var.arg_name 和 ngx.var.arg_password 獲取查詢參數,并且與 SQL 語句格式進行字符串拼接,最終 sql_normal 會是這個樣子的:
~~~
local sql_normal = [[select id, name from user where name='person' and password='12345' limit 1;]]
~~~
正常情況下,如果 person 賬號存在并且 password 是 12345,那么sql執行結果就應該是能返回id號的。這個接口如果暴露在攻擊者面前,那么攻擊者很可能會讓參數這樣傳入:
~~~
name="' or ''='"
password="' or ''='"
~~~
那么這個 sql_normal 就會變成一個永遠都能執行成功的語句了。
~~~
local sql_normal = [[select id, name from user where name='' or ''='' and password='' or ''='' limit 1;]]
~~~
這就是一個簡單的 sql inject (注入)的案例,那么問題來了,面對這么兇猛的攻擊者,我們有什么辦法防止這種 SQL 注入呢?
很簡單,我們只要 把 傳入參數的變量 做一次字符轉義,把不該作為破壞SQL查詢語句結構的雙引號或者單引號等做轉義,把 ' 轉義成 \',那么變量 name 和 password 的內容還是乖乖的作為查詢條件傳入,他們再也不能為非作歹了。
那么怎么做到字符轉義呢?要知道每個數據庫支持的SQL語句格式都不太一樣啊,尤其是雙引號和單引號的應用上。有幾個選擇:
~~~
ndk.set_var.set_quote_sql_str()
ndk.set_var.set_quote_pgsql_str()
ngx.quote_sql_str()
~~~
這三個函數,前面兩個是 ndk.set_var 跳轉調用,其實是 HttpSetMiscModule 這個模塊提供的函數,是一個 C 模塊實現的函數,ndk.set_var.set_quote_sql_str() 是用于 MySQL 格式的 SQL 語句字符轉義,而 set_quote_pgsql_str 是用于 PostgreSQL 格式的 SQL 語句字符轉義。最后 ngx.quote_sql_str 是一個 ngx_lua 模塊中實現的函數,也是用于 MySQL 格式的 SQL 語句字符轉義。
讓我們看看代碼怎么寫:
~~~
local name = ngx.quote_sql_str(ngx.var.arg_name)
local password = ngx.quote_sql_str(ngx.var.arg_password)
local sql_normal = [[select id, name from user where name=]] .. name .. [[ and password=]] .. password .. [[ limit 1;]]
local res = ngx.location.capture('/postgres',
{ args = {sql = sql } }
)
local body = json.decode(res.body)
if (table.getn(res) > 0) {
return res[1];
}
return nil;
~~~
注意上述代碼有兩個變化:
~~~
* 用 ngx.quote_sql_str 把 ngx.var.arg_name 和 ngx.var.arg_password 包了一層,把返回值作為 sql 語句拼湊起來。
* 原本在 sql 語句中添加的單引號去掉了,因為 ngx.quote_sql_str 的返回值正確的帶上引號了。
~~~
這樣子已經可以抵御 SQL 注入的攻擊手段了,但開發過程中需要不斷的產生新功能新代碼,這時候也一定注意不要忽視 SQL 注入的防護,安全防御代碼就想織網一樣,只要有一處漏洞,魚兒可就游走了。
- 序
- Lua簡介
- Lua環境搭建
- 基礎數據類型
- 表達式
- 控制結構
- if/else
- while
- repeat
- 控制結構for的使用
- break,return
- Lua函數
- 函數的定義
- 函數的參數
- 函數的返回值
- 函數回調
- 模塊
- String庫
- Table庫
- 日期時間函數
- 數學庫函數
- 文件操作
- 元表
- 面向對象編程
- FFI
- LuaRestyRedisLibrary
- select+set_keepalive組合操作引起的數據讀寫錯誤
- redis接口的二次封裝(簡化建連、拆連等細節)
- redis接口的二次封裝(發布訂閱)
- pipeline壓縮請求數量
- script壓縮復雜請求
- LuaCjsonLibrary
- json解析的異常捕獲
- 稀疏數組
- 空table編碼為array還是object
- 跨平臺的庫選擇
- PostgresNginxModule
- 調用方式簡介
- 不支持事務
- 超時
- 健康監測
- SQL注入
- LuaNginxModule
- 執行階段概念
- 正確的記錄日志
- 熱裝載代碼
- 阻塞操作
- 緩存
- sleep
- 定時任務
- 禁止某些終端訪問
- 請求返回后繼續執行
- 調試
- 調用其他C函數動態庫
- 我的lua代碼需要調優么
- 變量的共享范圍
- 動態限速
- shared.dict 非隊列性質
- 如何添加自己的lua api
- 正確使用長鏈接
- 如何引用第三方resty庫
- 使用動態DNS來完成HTTP請求
- 緩存失效風暴
- Lua
- 下標從1開始
- 局部變量
- 判斷數組大小
- 非空判斷
- 正則表達式
- 不用標準庫
- 虛變量
- 函數在調用代碼前定義
- 抵制使用module()函數來定義Lua模塊
- 點號與冒號操作符的區別
- 測試
- 單元測試
- API測試
- 性能測試
- 持續集成
- 灰度發布
- web服務
- API的設計
- 數據合法性檢測
- 協議無痛升級
- 代碼規范
- 連接池
- c10k編程
- TIME_WAIT問題
- 與Docker使用的網絡瓶頸
- 火焰圖
- 什么時候使用
- 顯示的是什么
- 如何安裝火焰圖生成工具
- 如何定位問題