2012-04-14 175 views
2

我想查找文本列以用戶給定字符串開頭的行,例如, SELECT * FROM users WHERE name LIKE 'rob%'但「rob」是未驗證的用戶輸入。如果用戶寫入一個包含特殊模式字符(如「rob_」)的字符串,它將匹配「robert42」和「rob_the_man」。我需要確保字符串是字面匹配的,我該怎麼做?我是否需要在應用程序級別處理轉義,還是更美麗的方式?如何在PostgreSQL中匹配模式時轉義字符串

我爲Go使用PostgreSQL 9.1和go-pgsql

回答

3

逃脫下劃線和百分比在模式中可以使用在like表達式中使用轉義字符:

SELECT * FROM users WHERE name LIKE replace(replace(user_input, '_', '\\_'), '%', '\\%'); 
+0

謝謝。我以類似的方式結束了這個過程(請參閱我的回答) – Betamos 2012-04-14 16:35:44

+1

我認爲它不處理出現在用戶輸入中的情況。我已經提交了另一個我希望覆蓋的答案。 – 2012-04-14 16:40:12

1

至於我可以與LIKE操作員告知只有特殊字符是百分比和下劃線,而且這些可以通過反斜槓手動轉義。這不是很漂亮,但它的作品。

SELECT * FROM users WHERE name LIKE 
regexp_replace('rob', '(%|_)', '\\\1', 'g') || '%'; 

我覺得奇怪的是PostgreSQL沒有這樣的函數。誰希望他們的用戶編寫自己的模式?

+0

我不記得在PostgreSQL郵件列表上討論的問題 - 有可能它以前沒有提出過。我懷疑提交補丁的人會被建議提供一個相應的函數來轉義數據以插入到正則表達式中,因爲在PostgreSQL用戶中,這些用戶似乎比LIKE更受歡迎(對於常規運算符表達匹配)。如果您想提交補丁,我很樂意爲您提供幫助,並提供審覈。 – kgrittn 2012-04-15 14:05:22

+0

兩個或三個'replace()'調用通常比單個'regexp_replace()'調用快得多。你應該與丹尼爾一起深思熟慮的答案。 – 2012-04-16 02:00:21

5

必須引用_和%字符才能在LIKE語句中進行字面匹配,這是無法解決的。選擇是在做客戶端還是服務器端(通常使用SQL替換(),請參見下文)。爲了在一般情況下獲得100%的權利,還有幾件事需要考慮。

默認情況下,在_或%之前使用的引號字符是反斜線(\),但可以使用緊跟在LIKE子句後面的ESCAPE子句進行更改。 在任何情況下,引號字符都必須在模式中重複兩次才能被逐字匹配爲一個字符。

例如:... WHERE field like 'john^%node1^^[email protected]%' ESCAPE '^'會匹配john%node1^node2.uccp @後面是任何內容。

反斜槓的默認選擇存在問題:當standard_conforming_strings關閉時(PG 9.1默認開啓,但以前的版本仍在廣泛使用中,這是一個需要考慮的問題),它已用於其他目的。

此外,如果在用戶輸入注入場景中對LIKE通配符進行了客戶端引用,那麼除了之外,它還包含到用戶輸入中已經需要的正常字符串引用。

一看go-pgsql示例就會告訴它它使用$ N樣式的佔位符來表示變量...因此,這裏試圖以某種通用的方式編寫它:它可以與standard_conforming_strings一起使用ON或OFF,使用服務器 - 側更換[%_],另一種引號字符,引用引號字符,並避免SQL注入:

db.Query("SELECT * from USERS where name like replace(replace(replace($1,'^','^^'),'%','^%'),'_','^_') ||'%' ESCAPE '^'", 
    variable_user_input); 
0

最好的答案是,你不應該插用戶輸入到SQL可言。即使轉義sql也是危險的。

以下使用go的db/sql庫說明了一個更安全的方法。用你的postgresql庫的等價物替換Prepare和Exec調用。

// The question mark tells the database server that we will provide 
// the LIKE parameter later in the Exec call 
sql := "SELECT * FROM users where name LIKE ?" 
// no need to escape since this won't be interpolated into the sql string. 
value := "%" + user_input 
// prepare the completely safe sql string. 
stmt, err := db.Prepare(sql) 
// Now execute that sql with the values for every occurence of the question mark. 
result, err := stmt.Exec(value) 

這樣做的好處是,用戶輸入可以安全地使用,而不用擔心它將sql注入到您運行的語句中。您還可以獲得將準備好的sql重複用於多個查詢的好處,這在某些情況下可以更有效。

+7

此解決方案不會讓用戶正確搜索包含文字「%」或「_」字符的值。例如,如果user_input等於「100%更好」,它將錯誤地匹配諸如「100倍好」的字符串。 – Ed4 2013-01-20 16:01:24