Rails SQLインジェクション
6257 ワード
バックエンド開発を研究するとき、私はSQL(および他のデータベース問い合わせ言語)を使用することが直接コードをはるかに速くするということを学びました.SQLは特にクエリを行うために構築されているので、それは確かに意味がありますが、私は実際にこれが実際にされている場合、実際に確認したことがない、それがされている場合、それはどのくらいの速さです.それで、私はテストにそれを置くことに決めました、そして、Ruby ActiveRecordベースのアプローチよりむしろ直接SQL注射を使用するならば、私のコードがより速く走らせるという仮説を検証するために、Ruby on Railsのミニプロジェクトを作成しました.何が起こったのです.
セットアップ
まず、テストシナリオを作成し、SQLとActiveRecordの実装を実装する必要がありました.私はプロジェクトを作成するための手順を行くつもりはないが、ここではハイライトです ユーザーと関連する注文は、データベースに格納されます 命令には、payerRange名と属性名 Orderモデルでは、指定されたユーザーに関連付けられた順序を抽出し、CreateDRADER 必要に応じて、メソッドを呼び出すときにpayerRage名を指定して結果をフィルタリングできます 私のデモユーザーは10関連付けられている これらの命令のうちの6つは、「Store 1」に等しいpayerRange名を持ちます 私のアプリとデータベースを設定した後、私は上記の箇条書きに記載されているメソッドをコード化する必要があります.
通常の実装( Ruby & ActiveRecord )
ここでは、このコードが直接SQLを注入せずにどのように見えるかを示します.
PayerRound名を指定すると、ソート前に結果をフィルター処理します.これは私のパフォーマンスを少し助け、テストをより公正にします.
SQLインジェクション実装
さて、まったく同じことをする方法を追加したいのですが、今回はRubyとActiveRecordに頼らずにSQLで結果を取得します.
テスト
今、私が準備をしているので、テストしましょう.このため、Rubyの宝石を使用しますbenchmark それぞれのメソッドを実行するのにかかる時間を計算する.これらは巨大な方法ではないので、私は結果をより簡単に操作することができるように何度もそれらを実行します.を使用します
' payerRange 'フィルタのないテスト
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
0.421875
1.609375
2.03125
インジェクション
0.203125
0.5
0.703125
削減
51.85 %
68.93 %
65.38 %
5000回繰り返した結果、次のデータが得られた.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
0.828125
3.21875
4.046875
インジェクション
0.6875
2.078125
2.765625
削減
16.98 %
35.44 %
31.66 %
最終的に10000回の繰り返しにより、以下のデータが得られた.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
1.890625
6.234375
8.125000
インジェクション
1.156250
2.625000
3.781250
削減
38.84 %
57.89 %
53.46 %
PayerRange名フィルタによるテスト
さて、PayerRange名がstore 1である場合にのみ、この値を返す必要条件を追加します.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
0.390625
0.9375
1.328125
インジェクション
0.09375
0.203125
0.296875
削減
76.00 %
78.33 %
77.65 %
5000回繰り返した結果、次のデータが得られた.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
1.234375
3.59375
4.828125
インジェクション
1.0625
1.3125
2.375
削減
13.92 %
63.48 %
50.81 %
最終的に10000回の繰り返しにより、以下のデータが得られた.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
3.28125
11.359375
14.640625
インジェクション
1.96875
4.203125
6.171875
削減
40.00 %
63.00 %
57.84 %
結果
このデータを使用すると、直接SQL注入がRuby & ActiveRecordより高速に動作するという仮説は、実際には、サポートされています.また、縮小図を見るとき、彼らはそれが実際に非常に高速だと示唆しています!プログラムが私のコンピュータで何を実行しているかを含む結果に影響を及ぼす他の要因があることを心に留めておくことは重要です、それで、2回同じテストを実行することは同じ結果を得ることが非常にありそうもありません.しかし非フィルタリングのための50.17 %の平均減少とフィルタリングされた結果のためのwhopping 62.10 %で、このSQL注入戦略が正しい状況で巨大なパフォーマンス後押しであるという疑問が、ありません.
ボーナス
上の私の分析について私を悩ましている何かが、ありました.SQLの戦略は、巨大なパフォーマンスのブーストだったが、それは確かにエッジを与えるイテレータメソッドをスキップするの利点があります.純粋なSQL対ActiveRecordの効果を測定するには、その調停因子を切り出す必要があります.そこで、同じテストの結果のテーブルですが、すべての並べ替えとフィルタリングを削除しました.これは単にuserCirus IDに基づいてユーザーを見つけ、特定の順序ですべての関連付けられた順序を取得します.
私は5000回の繰り返しでテストをした.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
1.96875
4.28125
6.25
インジェクション
1.171875
2.546875
3.71875
削減
40.48 %
40.51 %
40.50 %
したがって、これはActiveRecordの上でSQLを直接使用することから、まだ大きい、少しより小さい、パフォーマンス利得があることを示唆します.仮説はまだ支持
セットアップ
まず、テストシナリオを作成し、SQLとActiveRecordの実装を実装する必要がありました.私はプロジェクトを作成するための手順を行くつもりはないが、ここではハイライトです
通常の実装( Ruby & ActiveRecord )
ここでは、このコードが直接SQLを注入せずにどのように見えるかを示します.
def self.sort_orders(user_id, payer_name = '*')
if payer_name == '*'
User.find(user_id).orders.sort {|a, b| a.created_at <=> b.created_at}
else
User.find(user_id).orders.filter{|t| t.payer_name == payer_name}.sort {|a, b| a.created_at <=> b.created_at}
end
end
したがって、PayerRange名が指定されているかどうかに関係なく、コードは、渡されたuserRound IDに基づいてユーザーインスタンスを見つけることによって開始します.PayerRage名が指定された場合、結果はPayerRange名が提供されたものと一致する結果を含めるためにこの時点でフィルタリングされます.最後に、任意の残りの結果をcreatedRAGEで昇順でソートされます.PayerRound名を指定すると、ソート前に結果をフィルター処理します.これは私のパフォーマンスを少し助け、テストをより公正にします.
SQLインジェクション実装
さて、まったく同じことをする方法を追加したいのですが、今回はRubyとActiveRecordに頼らずにSQLで結果を取得します.
def self.sort_orders_SQL(user_id, payer_name = '*')
if payer_name == '*'
self.find_by_sql(["SELECT * FROM orders WHERE user_id = ? ORDER BY created_at ASC", user_id].flatten)
else
self.find_by_sql(["SELECT * FROM orders WHERE user_id = ? AND payer_name = ? ORDER BY created_at ASC", user_id, payer_name].flatten)
end
end
前述のように、これは全く同じことをします.つの主要な利点;しかし、SQLクエリで順序ロジックを追加することができます.payerRange名が指定されている場合、フィルタロジックに対する同じ取引.これは、データベースクエリを注入するための優れたユースケースです!テスト
今、私が準備をしているので、テストしましょう.このため、Rubyの宝石を使用しますbenchmark それぞれのメソッドを実行するのにかかる時間を計算する.これらは巨大な方法ではないので、私は結果をより簡単に操作することができるように何度もそれらを実行します.を使用します
bm
両方のテストを同時に実行する方法.私は各方法を1000回実行することから始めます、そして、楽しみのために、私は5000と10000の繰り返しで同じテスト方法を走らせます.' payerRange 'フィルタのないテスト
Benchmark.bm do |benchmark|
benchmark.report("no-inject") do // label denoting no SQL injection
1000.times do
Order.sort_orders(User.first.id)
end
end
benchmark.report("injectSQL") do // label denoting SQL injection used
1000.times do
Order.sort_orders_SQL(User.first.id)
end
end
end
テストを1000回ごとに以下のデータで実行します(注:縮小カラムは計算され、ベンチマークのGEMによって提供されません)システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
0.421875
1.609375
2.03125
インジェクション
0.203125
0.5
0.703125
削減
51.85 %
68.93 %
65.38 %
5000回繰り返した結果、次のデータが得られた.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
0.828125
3.21875
4.046875
インジェクション
0.6875
2.078125
2.765625
削減
16.98 %
35.44 %
31.66 %
最終的に10000回の繰り返しにより、以下のデータが得られた.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
1.890625
6.234375
8.125000
インジェクション
1.156250
2.625000
3.781250
削減
38.84 %
57.89 %
53.46 %
PayerRange名フィルタによるテスト
さて、PayerRange名がstore 1である場合にのみ、この値を返す必要条件を追加します.
Benchmark.bm do |benchmark|
benchmark.report("no-inject") do // label denoting no SQL injection
1000.times do
Order.sort_orders(User.first.id, "Store1")
end
end
benchmark.report("injectSQL") do // label denoting SQL injection used
1000.times do
Order.sort_orders_SQL(User.first.id, "Store1")
end
end
end
以下のデータでテスト結果を1000回実行しますシステムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
0.390625
0.9375
1.328125
インジェクション
0.09375
0.203125
0.296875
削減
76.00 %
78.33 %
77.65 %
5000回繰り返した結果、次のデータが得られた.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
1.234375
3.59375
4.828125
インジェクション
1.0625
1.3125
2.375
削減
13.92 %
63.48 %
50.81 %
最終的に10000回の繰り返しにより、以下のデータが得られた.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
3.28125
11.359375
14.640625
インジェクション
1.96875
4.203125
6.171875
削減
40.00 %
63.00 %
57.84 %
結果
このデータを使用すると、直接SQL注入がRuby & ActiveRecordより高速に動作するという仮説は、実際には、サポートされています.また、縮小図を見るとき、彼らはそれが実際に非常に高速だと示唆しています!プログラムが私のコンピュータで何を実行しているかを含む結果に影響を及ぼす他の要因があることを心に留めておくことは重要です、それで、2回同じテストを実行することは同じ結果を得ることが非常にありそうもありません.しかし非フィルタリングのための50.17 %の平均減少とフィルタリングされた結果のためのwhopping 62.10 %で、このSQL注入戦略が正しい状況で巨大なパフォーマンス後押しであるという疑問が、ありません.
ボーナス
上の私の分析について私を悩ましている何かが、ありました.SQLの戦略は、巨大なパフォーマンスのブーストだったが、それは確かにエッジを与えるイテレータメソッドをスキップするの利点があります.純粋なSQL対ActiveRecordの効果を測定するには、その調停因子を切り出す必要があります.そこで、同じテストの結果のテーブルですが、すべての並べ替えとフィルタリングを削除しました.これは単にuserCirus IDに基づいてユーザーを見つけ、特定の順序ですべての関連付けられた順序を取得します.
私は5000回の繰り返しでテストをした.
システムCPU時間
ユーザCPU時間
合計CPU時間
注入しない
1.96875
4.28125
6.25
インジェクション
1.171875
2.546875
3.71875
削減
40.48 %
40.51 %
40.50 %
したがって、これはActiveRecordの上でSQLを直接使用することから、まだ大きい、少しより小さい、パフォーマンス利得があることを示唆します.仮説はまだ支持
Reference
この問題について(Rails SQLインジェクション), 我々は、より多くの情報をここで見つけました https://dev.to/dhintz89/rails-sql-injections-451eテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol