標籤:text exe 使用者輸入 from 替換 指令 例子 *** post
一般情況下,SQL查詢是相對固定的,一條語句變化的可能只是條件值,比如之前要求查詢二年級學生資訊,而後面需要查詢三年級的資訊,這樣的查詢一般查詢的列不變,後面的條件只有值在變化,針對這種查詢可以使用參數化查詢的方式來提高效率,也可以時SQL操作更加安全,從根本上杜絕SQL注入的問題。
參數化查詢的優勢:
- 提高效率:之前說過,資料庫在執行SQL的過程中,每次都會經過SQL的解析,編譯,調用對應的資料庫組件,這樣如果執行多次同樣類型的SQL語句,解析,編譯的過程明顯是在浪費資源,而參數化查詢就是使用編譯好的過程(也就是提前告訴資料庫要調用哪些資料庫組件),這樣就跳過了對SQL語句的解析,編譯過程,提高了效率(這個過程我覺得有點類似於C/C++語言的編譯執行與指令碼語言的解釋執行)。
- 更加安全:從安全編程的角度來說,對於防範SQL注入方面,它比關鍵字過濾更有效,實現起來也更加方便。
科普SQL注入和安全編程
所謂SQL注入,就是通過把SQL命令插入到Web表單提交或輸入欄位名或頁面請求的查詢字串,最終達到欺騙伺服器執行惡意的SQL命令。舉個例子來說在使用者登入時會輸入使用者名稱密碼,這個時候在後台就可以執行這樣的SQL語句
SQL select count(*) from user where username = ‘haha‘ and password = ‘123456‘
只有輸入對了使用者名稱和密碼才能登入,但是如果沒有對使用者輸入進行校正,當使用者輸入一些SQL中的語句,而後台直接將使用者輸入進行拼接並執行,就會發生注入,比如此時使用者輸入 *** ‘haha‘ or 1 = 1 -- *** ,此時再後台執行的sql語句就變成了這樣:
SQL select count(*) from user where username = ‘haha‘ or 1 = 1 -- and password = ‘‘
這樣使用者就可以不用密碼,直接使用使用者名稱就登入了。而防範這類攻擊,一般採用的是關鍵字過濾的方式,但是關鍵字過濾並不能杜絕這類工具,當一時疏忽忘記了過濾某個關鍵字仍然會產生這類問題。而且關鍵字過濾一般採用Regex,而Regex並不是一般人可以駕馭的。而防範SQL注入最簡單也是最一勞永逸的方式就是參數化查詢。
發生SQL注入一般的原因是程式將使用者輸入當做SQL語句的一部分進行執行,但是參數化查詢它只是將使用者輸入當做參數,當做查詢的條件,從資料庫的層面上來說,它不對應於具體的資料庫組件,它只是一組資料,而不會執行。這裡可以簡單的將傳統的SQL拼接方式理解為C語言中的宏,宏也可以有參數,但是它不對參數進行校正,只是簡單的進行替換,那麼我可以使用一些指令作為參數傳入,但是函數就不一樣,函數的參數就是具體類型的變數或者常量。所以參數化查詢從根本上解決的SQL注入的問題。
參數化查詢的使用
前面說了這麼多參數化查詢的好處,那麼到底怎麼使用它呢?
在Java等語言中內建了資料庫操作,而對於C/C++來說,它並沒有提供這方方面的標準。不同的平台有自己獨特的一套機制,但是從總體來說,思想是共通的,只是文法上的不同,這裡主要是說明OLEDB中的使用方式。
- 使用“?”符將SQL語句中的條件值常量進行替換,組成一個新的SQL語句,比如上面登入的查詢語句可以寫成
SQL select count(*) from user where username = ? and password = ?
- 調用ICommandText的SetCommandText設定sql語句。
- 調用ICommandParpare的Prepare方法對含有"?"的語句進行預先處理
- 調用ICommandWithParameters方法的GetParameterInfo方法擷取參數詳細資料的DBPARAMINFO結構(類似於DBCOLUMNINFO)
- 分配對應大小的DBBINDING緩衝用來儲存每個參數的綁定資訊
- 調用IAccessor的CreateAccessor方法建立對應的訪問器
- 為參數分配緩衝,設定合適的參數後準備DBPARAMS結構
- 調用ICommandText的Execute方法並將DBPARAMS結構的指標作為參數傳入。
- 操作返回的結果集對象
typedef struct tagDBPROPIDSET { DBPROPID * rgPropertyIDs; ULONG cPropertyIDs; GUID guidPropertySet;} DBPROPIDSET;
DBPARAMS結構的定義如下:
typedef struct tagDBPARAMS{ void *pData; DB_UPARAMS cParamSets; HACCESSOR hAccessor;} DBPARAMS;
- pData是儲存參數資訊的緩衝;
- cParamSets: 表示又多少個參數
- hAccessor: 之前擷取到的綁定結構的訪問器控制代碼
下面是一個使用的例子:
BOOL QueryData(LPOLESTR pQueryStr, IOpenRowset* pIOpenRowset, IRowset* &pIRowset){ IAccessor *pParamAccessor = NULL; //與參數化查詢相關的訪問器介面 LPOLESTR pSql = _T("Select * From aa26 Where Left(aac031,2) = ?"); //參數化查詢語句 BOOL bRet = FALSE; DB_UPARAMS uParams = 0; DBPARAMINFO* rgParamInfo = NULL; LPOLESTR pParamBuffer = NULL; DWORD dwOffset = 0; DBBINDING *rgParamBinding = NULL; HACCESSOR hAccessor = NULL; DBPARAMS dbParams = {0}; DBBINDSTATUS *pdbBindStatus = NULL; //設定SQL hRes = pICommandText->SetCommandText(DBGUID_DEFAULT, pSql); //預先處理SQL命令 pICommandPrepare->Prepare(0); hRes = pICommandText->QueryInterface(IID_ICommandPrepare, (void**)&pICommandPrepare); //擷取參數資訊 hRes = pICommandText->QueryInterface(IID_ICommandWithParameters, (void**)&pICommandWithParameters); COM_SUCCESS(hRes, _T("查詢介面ICommandWithParameters失敗,錯誤碼為:%08x\n"), hRes); hRes = pICommandWithParameters->GetParameterInfo(&uParams, &rgParamInfo, &pParamBuffer); COM_SUCCESS(hRes, _T("擷取參數資訊失敗,錯誤碼為:%08x\n"), hRes); rgParamBinding = (DBBINDING*)MALLOC(sizeof(DBBINDING) * uParams); ZeroMemory(rgParamBinding, sizeof(DBBINDING) * uParams); //綁定參數資訊 for (int i = 0; i < uParams; i++) { rgParamBinding[i].bPrecision = rgParamInfo[i].bPrecision; rgParamBinding[i].bScale = rgParamInfo[i].bScale; rgParamBinding[i].cbMaxLen = 7 * sizeof(WCHAR); //行政區編號最大長度為6 rgParamBinding[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED; rgParamBinding[i].dwPart = DBPART_LENGTH | DBPART_VALUE; rgParamBinding[i].eParamIO = DBPARAMIO_INPUT; rgParamBinding[i].iOrdinal = rgParamInfo[i].iOrdinal; rgParamBinding[i].obLength = dwOffset; rgParamBinding[i].obStatus = 0; rgParamBinding[i].obValue = dwOffset + sizeof(ULONG); rgParamBinding[i].wType = DBTYPE_WSTR; dwOffset = dwOffset + sizeof(ULONG) + rgParamBinding[i].cbMaxLen; dwOffset = UPROUND(dwOffset); } //擷取訪問器 pdbBindStatus = (DBBINDSTATUS*)MALLOC(uParams * sizeof(DBBINDSTATUS)); ZeroMemory(pdbBindStatus, uParams * sizeof(DBBINDSTATUS)) pParamAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, uParams, rgParamBinding, dwOffset, &hAccessor, pdbBindStatus); COM_SUCCESS(hRes, _T("擷取參數訪問器失敗,錯誤碼為:%08x\n"), hRes); //準備參數 dbParams.pData = MALLOC(dwOffset); ZeroMemory(dbParams.pData, dwOffset); dbParams.cParamSets = uParams; dbParams.hAccessor = hAccessor; for (int i = 0; i < uParams; i++) { *(ULONG*)((BYTE*)dbParams.pData + rgParamBinding[i].obLength) = _tcslen(pQueryStr) * sizeof(WCHAR); StringCchCopy((LPTSTR)((BYTE*)dbParams.pData + rgParamBinding[i].obValue), _tcslen(pQueryStr) + 1, pQueryStr); } //執行SQL hRes = pICommandText->Execute(NULL, IID_IRowset, &dbParams, NULL, (IUnknown**)&pIRowset); return bRet;}
完整代碼
OLEDB 參數化查詢