アプリケーション開発者は、ストアドプロシージャ内にデータベース操作を格納することで最適なパフォーマンスが得られ、SQLインジェクション攻撃から保護されると長い間信じてきました。また、これらの利点は、データベースロジックのメンテナンス、テスト、および別のベンダーへの移行に関連する追加コストに見合う価値があると考えられていました。近年、開発者がこれらの長年の前提に疑問を持ち始めたため、流れはストアドプロシージャ(またはproc)からHibernateやEntity Frameworkなどのオブジェクトリレーショナルマッパー(ORM)に変わりつつあります。
ストアドプロシージャは時代遅れのツールですか?の記事では、アプリケーションコードとORMを優先してストアドプロシージャを避けるいくつかの理由を取り上げました。今週は、上記で紹介した2つの神話を調査し、今日でもそれらが精査に耐えられるかどうかを見ていきます。
パフォーマンス上の利点
インターネットの黎明期には、パフォーマンスを向上させるためにネットワークトラフィックを最小限に抑えることが一般的でした。ストアドプロシージャは、完全なSQLステートメントではなく、proc名とパラメータのみをサーバーに転送する必要があるため、ネットワークトラフィックの削減に役立ちました。一部の運用クエリの複雑さと長さを考慮すると、これらの利点は時にはかなりのものになる可能性があります。今日、このアプローチから得られる利益は、同じリクエストで同じパラメータを使用して同じプロシージャを2回または3回呼び出す可能性が非常に高いという事実によって、簡単に相殺されます。一方、ORMはそのIDマップを調べて、その結果セットを既に取得していることを認識するため、別の往復を行う必要はありません。さらに、ストアドプロシージャはサーバーにキャッシュされるが、アドホックSQLはキャッシュされないという主張は、Frans Boumaのブログ投稿、Stored Procedures are bad, m'kay?, m'kay?で破られた神話であることに注意してください。
ストアドプロシージャとSQLインジェクション
ストアドプロシージャはデータを命令から分離するため、SQLインジェクションに対する自然な保護を提供するとよく言われます。これは、プレースホルダーを置き換える入力パラメーターを介して生の文字列が渡されるストアドプロシージャ内で開発者が動的SQLを使用しない限り、当てはまります。これは、プレースホルダーを置き換える入力パラメータを介して生の文字列が渡されるストアドプロシージャ内で開発者が動的SQLを使用しない限り、当てはまります。これは、SQLインジェクションに対してデータベースを開く方法を正確に示す、不適切に作成されたprocです:
create procedure GetStudents(@School nvarchar(50)) as begin declare @sql nvarchar(100) set @sql = 'SELECT STUDENT FROM SCHOOL WHERE SCHOOL LIKE ' + @School exec @sql end
パラメータ化されたクエリを使用することによって、SQLインジェクションの脆弱性を排除するSQLを作成できます。Python、TypeScript、Javaなどのプログラム言語で記述された次のようなプリペアードステートメントは、クエリで安全に使用できるようにユーザー入力をサニタイズできます:
String sql = "SELECT STUDENT FROM SCHOOL WHERE SCHOOL LIKE ? "; PreparedStatement prepStmt = conn.prepareStatement(sql); prepStmt.setString(1, "Waterloo%"); ResultSet rs = prepStmt.executeQuery();
ここでの教訓は、SQLインジェクションに対する保護は、ストアドプロシージャ自体の利点ではなく、むしろSQL文字列を連結しないという規則です。
今後
このブログでは、ストアドプロシージャについて、今日ではあまり当てはまらないいくつかの長年の仮定について説明しました。それだけではストアドプロシージャの時流から飛び降りる十分な理由にはなりませんが、アプリケーションアーキテクチャを再評価する時期に来ている可能性があることを強く示唆しています。