OLEDB 參數化查詢

來源:互聯網
上載者:User

標籤:text   exe   使用者輸入   from   替換   指令   例子   ***   post   

一般情況下,SQL查詢是相對固定的,一條語句變化的可能只是條件值,比如之前要求查詢二年級學生資訊,而後面需要查詢三年級的資訊,這樣的查詢一般查詢的列不變,後面的條件只有值在變化,針對這種查詢可以使用參數化查詢的方式來提高效率,也可以時SQL操作更加安全,從根本上杜絕SQL注入的問題。

參數化查詢的優勢:
  1. 提高效率:之前說過,資料庫在執行SQL的過程中,每次都會經過SQL的解析,編譯,調用對應的資料庫組件,這樣如果執行多次同樣類型的SQL語句,解析,編譯的過程明顯是在浪費資源,而參數化查詢就是使用編譯好的過程(也就是提前告訴資料庫要調用哪些資料庫組件),這樣就跳過了對SQL語句的解析,編譯過程,提高了效率(這個過程我覺得有點類似於C/C++語言的編譯執行與指令碼語言的解釋執行)。
  2. 更加安全:從安全編程的角度來說,對於防範SQL注入方面,它比關鍵字過濾更有效,實現起來也更加方便。
科普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語句的一部分進行執行,但是參數化查詢它只是將使用者輸入當做參數,當做查詢的條件,從資料庫的層面上來說,它不對應於具體的資料庫組件,它只是一組資料,而不會執行。這裡可以簡單的將傳統的SQL拼接方式理解為C語言中的宏,宏也可以有參數,但是它不對參數進行校正,只是簡單的進行替換,那麼我可以使用一些指令作為參數傳入,但是函數就不一樣,函數的參數就是具體類型的變數或者常量。所以參數化查詢從根本上解決的SQL注入的問題。

參數化查詢的使用

前面說了這麼多參數化查詢的好處,那麼到底怎麼使用它呢?
在Java等語言中內建了資料庫操作,而對於C/C++來說,它並沒有提供這方方面的標準。不同的平台有自己獨特的一套機制,但是從總體來說,思想是共通的,只是文法上的不同,這裡主要是說明OLEDB中的使用方式。

  1. 使用“?”符將SQL語句中的條件值常量進行替換,組成一個新的SQL語句,比如上面登入的查詢語句可以寫成
    SQL select count(*) from user where username = ? and password = ?
  2. 調用ICommandText的SetCommandText設定sql語句。
  3. 調用ICommandParpare的Prepare方法對含有"?"的語句進行預先處理
  4. 調用ICommandWithParameters方法的GetParameterInfo方法擷取參數詳細資料的DBPARAMINFO結構(類似於DBCOLUMNINFO)
  5. 分配對應大小的DBBINDING緩衝用來儲存每個參數的綁定資訊
  6. 調用IAccessor的CreateAccessor方法建立對應的訪問器
  7. 為參數分配緩衝,設定合適的參數後準備DBPARAMS結構
  8. 調用ICommandText的Execute方法並將DBPARAMS結構的指標作為參數傳入。
  9. 操作返回的結果集對象
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 參數化查詢

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.