0

我對一個Postgres數據庫以下的find_by_sql查詢:Rails - 如何防止postgres find_by_sql查詢的sql注入?

pick_ids = picks.pluck(:id).join(',') 

Pick.find_by_sql("WITH cte1 AS (SELECT DISTINCT user_id, state, pick FROM picks 
WHERE id IN (#{pick_ids})), cte2 AS (SELECT user_id, coalesce(sum(amount_won), 0) 
as picks_total_won FROM picks WHERE id IN (#{pick_ids}) GROUP BY user_id), cte3 AS 
(SELECT user_id, COUNT(CASE WHEN state = 'won' then 1 ELSE null END) AS picks_won, 
FROM cte1 GROUP BY user_id) SELECT cte2.picks_total_won, cte3.picks_won, 
FROM cte2 INNER JOIN cte3 ON cte2.user_id = cte3.user_id") 

當我嘗試將其參數化(即,... pick_ids,pick_ids?),但是,我得到以下錯誤:

ArgumentError: wrong number of arguments (3 for 1..2) 

(1)你能參數化一個find_by_sql查詢嗎?如果是這樣,怎麼樣? (2)如果查詢永遠不會收到用戶輸入的參數,你甚至需要擔心SQL注入嗎?

回答

3

首先,你可能想使用一個%Q{...}字符串和一些格式化你的SQL,以避免不可讀的混亂:

Pick.find_by_sql(%Q{ 
    WITH 
    cte1 AS (
     SELECT DISTINCT user_id, state, pick 
     FROM picks 
     WHERE id IN (#{pick_ids}) 
    ), 
    cte2 AS (
     SELECT user_id, coalesce(sum(amount_won), 0) as picks_total_won 
     FROM picks 
     WHERE id IN (#{pick_ids}) 
     GROUP BY user_id 
    ), 
    cte3 AS (
     SELECT user_id, COUNT(CASE WHEN state = 'won' then 1 ELSE null END) AS picks_won, 
     FROM cte1 
     GROUP BY user_id 
    ) 
    SELECT cte2.picks_total_won, cte3.picks_won 
    FROM cte2 
    JOIN cte3 ON cte2.user_id = cte3.user_id 
}) 

視您picks從何而來,你也許能避免插值完全並嵌入SQL以生成pick_ids(可能使用其他CTE)。

如果您需要從外界飼料中​​pick_ids,那麼你可以使用佔位符與find_by_sql但界面是有點匪夷所思:你有一個數組傳遞給find_by_sql

Pick.find_by_sql([%Q{ 
    WITH 
    cte1 AS (
     SELECT DISTINCT user_id, state, pick 
     FROM picks 
     WHERE id IN (:pick_ids) 
    ), 
    cte2 AS (
     SELECT user_id, coalesce(sum(amount_won), 0) as picks_total_won 
     FROM picks 
     WHERE id IN (:pick_ids) 
     GROUP BY user_id 
    ), 
    cte3 AS (
     SELECT user_id, COUNT(CASE WHEN state = 'won' then 1 ELSE null END) AS picks_won, 
     FROM cte1 
     GROUP BY user_id 
    ) 
    SELECT cte2.picks_total_won, cte3.picks_won 
    FROM cte2 
    JOIN cte3 ON cte2.user_id = cte3.user_id 
}, :pick_ids => some_array_of_ids]) 

注位置在參數列表中的[]

+0

太神奇了!很好的答案...應該對他人有用 – keruilin

0

您也可以使用Pick.sanitize(str)來插入任意文本。

所以,暫時忽略所有其他的方式來改善這種畝提到的查詢,這將是:

Pick.find_by_sql("WITH cte1 AS (SELECT DISTINCT user_id, state, pick FROM picks 
WHERE id IN (#{Pick.sanitize pick_ids})), cte2 AS (SELECT user_id, coalesce(sum(amount_won), 0) 
as picks_total_won FROM picks WHERE id IN (#{Pick.sanitize pick_ids}) GROUP BY user_id), cte3 AS 
(SELECT user_id, COUNT(CASE WHEN state = 'won' then 1 ELSE null END) AS picks_won, 
FROM cte1 GROUP BY user_id) SELECT cte2.picks_total_won, cte3.picks_won, 
FROM cte2 INNER JOIN cte3 ON cte2.user_id = cte3.user_id")