@nqounetです。
連載「URL短縮サポーターを作ってみよう」の第7回です。
前回の振り返り
第6回では、URLを保存するためのテーブル構造を設計し、CREATE TABLE文で実際にテーブルを作成しました。
前回学んだ内容を簡単に振り返ります。
- テーブルはデータを表形式で管理する単位であり、カラムはデータの項目を表す
- URL短縮に必要なカラムはid、original_url、short_code、created_atの4つである
CREATE TABLE IF NOT EXISTSで冪等なテーブル作成ができる$dbh->do($sql)でPerlからDDL(データ定義言語)を実行できる
今回は、いよいよ短縮URLをデータベースに保存する方法を学びます。
今回のゴール
第7回では、以下を達成することを目標とします。
- INSERT文でデータを登録する方法を理解する
- プレースホルダ(
?)を使って安全にデータを挿入する - 簡易バリデーション(空文字チェック)でエラーを防ぐ
URLをデータベースに登録したい!
タカシさんの期待
前回、テーブルを作成したタカシさん。次の疑問が湧いてきました。
「テーブルはできたけど、ここにURLを保存するにはどうすればいいの?」
データベースにデータを追加するには、INSERT文を使います。今回はこのINSERT文の書き方と、セキュリティを考慮した安全な実行方法を学びます。
INSERT文の基本
INSERT文は、テーブルに新しいレコード(行)を追加するSQL文です。基本的な構文は以下のとおりです。
| |
今回作成したurlsテーブルにデータを追加する場合、以下のようになります。
| |
idとcreated_atは省略しています。idはINTEGER PRIMARY KEYなので自動採番され、created_atはDEFAULT CURRENT_TIMESTAMPなので現在日時が自動設定されるためです。
プレースホルダで安全に実行する
なぜプレースホルダが必要なのか
前回の$dbh->do($sql)を使えばINSERT文も実行できます。しかし、ユーザーからの入力をそのままSQL文に埋め込むのは危険です。
例えば、ユーザーが入力したURLをそのままSQL文に埋め込むコードを考えてみましょう。
| |
もしユーザーが$urlに'); DELETE FROM urls; --のような悪意のある文字列を入力したらどうなるでしょうか。SQL文は以下のように解釈されてしまいます。
| |
これはSQLインジェクションと呼ばれる攻撃手法で、データベースの内容を改ざんしたり、削除したりされてしまう可能性があります。
プレースホルダとは
プレースホルダ(?)を使うと、この問題を解決できます。プレースホルダは値の入る場所を示す記号で、実際の値は別途安全に渡されます。
| |
このコードでは、2つの?がプレースホルダです。prepareメソッドはSQL文を解析してステートメントハンドル($sth)を返します。この時点ではまだ値は設定されていません。
executeで値を渡す
準備したステートメントに実際の値を渡して実行するには、executeメソッドを使います。
| |
executeの引数として渡された値は、DBIが自動的にエスケープ処理を行います。これにより、どんな文字列が入力されてもSQL文の構造が壊れることはありません。
プレースホルダを使う利点は以下のとおりです。
- SQLインジェクション攻撃を防げる
- 特殊文字(シングルクォートなど)を含む値も安全に扱える
- コードが読みやすくなる
簡易バリデーションを追加する
空のURLは拒否したい
データベースに保存する前に、入力値が適切かどうかをチェックすることも重要です。今回は最も基本的なバリデーションとして、空文字チェックを追加します。
| |
この簡易バリデーションにより、空のURLが登録されることを防ぎます。より高度なバリデーション(URLの形式チェックなど)は、後の回で扱います。
実際のコードを見てみよう
URLを登録するスクリプト
ここまでの内容を組み合わせた完全なスクリプトを見てみましょう。
| |
コードの流れを解説します。
sequenceDiagram participant User as Browser participant App as Mojolicious participant Short as URLShortener participant DB as SQLite User->>App: POST /shorten (url) App->>Short: shorten(url) Short->>Short: validate url Short->>Short: generate code (sha1_hex -> substr) Short->>DB: INSERT original_url, short_code DB-->>Short: OK Short-->>App: return code App-->>User: render result (short URL)
データベース接続
| |
第5回で学んだ接続コードです。urls.dbファイルに接続します。
簡易バリデーション
| |
URLが空でないかをチェックします。空の場合はエラーメッセージを出力してスクリプトを終了します。
短縮コード生成
| |
第4回で学んだハッシュ関数を使って、URLから6文字の短縮コードを生成します。
INSERT文の実行
| |
プレースホルダを使った安全なINSERT文の実行です。prepareでSQL文を準備し、executeで実際の値を渡して実行します。
動作確認
スクリプトを実行する
上記のコードをinsert_url.plとして保存し、ターミナルで実行してください。
| |
以下のような出力が表示されれば成功です。
| |
短縮コードは入力URLによって異なります。
データベースの内容を確認する
SQLiteのコマンドラインツールでデータが登録されたことを確認できます。
| |
以下のような出力が表示されれば、データが正しく保存されています。
| |
idが自動採番され、created_atに現在日時が設定されていることがわかります。
まとめ
今回学んだこと
第7回では、以下のことを学びました。
- INSERT文でテーブルにデータを追加できる
- プレースホルダ(
?)を使うことでSQLインジェクション攻撃を防げる $dbh->prepare($sql)でステートメントを準備し、$sth->execute($値1, $値2)で実行する- 簡易バリデーション(
if (!$url) { ... })で空文字入力を防げる
次回予告
次回は「短縮URLへアクセス! — 動的ルーティング」をテーマに、/:code形式のURLでアクセスを受け付ける方法を学びます。Mojolicious::Liteのプレースホルダルーティングで動的なパスを処理する仕組みを理解しましょう。お楽しみに。
