𝗗𝗼𝗻'𝘁 𝗣𝗮𝗿𝘀𝗲 𝗦𝗤𝗟 𝘁𝗼 𝗠𝗮𝗸𝗲 𝗮 𝗤𝘂𝗲𝗿𝘆 𝗥𝘂𝗻𝗻𝗲𝗿 𝗥𝗲𝗮𝗱-𝗢𝗻𝗹𝘆 (クエリランナーを読み取り専用にするためにSQLをパースしてはいけない)
SQL文字列内のキーワードをチェックすることでデータベースを保護しようとするのは、もうやめましょう。
SQLを実行するツールを構築する場合、読み取り専用モードが必要になります。誤ったUPDATEによってデータが削除されるのを防ぎたいはずです。その際、最初に思いつくのはDELETEやDROPといった単語をブロックすることかもしれません。
ですが、それはやってはいけません。
文字列のチェックは簡単に回避できてしまいます。ユーザーはWITH句を使ってDELETEを隠したり、コメントを使ってコマンドを隠したりできます。また、テーブルに書き込みを行う関数を呼び出すことも可能です。結局、いたちごっこの負け戦に陥ることになります。
セキュリティはデータベースに任せましょう。
Postgresには、このための組み込み機能があります。トランザクションを読み取り専用(read-only)として宣言できるのです。そうすれば、サーバーはあらゆる書き込みコマンドを拒否します。これにはCTE、関数、DDLも含まれます。
Pythonでこれを正しく実装する方法は以下の通りです:
autocommitをFalseに設定して、実際のトランザクションを使用します。- 最初のコマンドとして
SET TRANSACTION READ ONLYを実行します。 - 長時間実行されるクエリがシステムをロックするのを防ぐため、
statement_timeoutを設定します。 - 最後に
rollback()を使用して、ロックとスナップショットを解放します。
このアプローチではSQLテキストを検査しません。クエリは書かれた通りにサーバーへ送られます。あなたはエンジンに対して、ルールを強制するように指示しているのです。
安全性には2つの要素が必要です:
- 書き込みからの保護:読み取り専用トランザクションを使用する。
- リソース乱用からの保護:タイムアウトと行制限を使用する。
読み取り専用のクエリであっても、巨大なJOINによってシステムをクラッシュさせる可能性があります。読み取り専用トランザクションは書き込みを停止させますが、大量のリソース消費を止めることはできません。アドホックなSQLを安全にするには、その両方が必要です。
SQLのパースはやめましょう。データベースにその役割を果たさせましょう。
Source: https://dev.to/hitoshi1964/dont-parse-sql-to-make-a-query-runner-read-only-b62