はじめに
以前、個人開発でURL短縮サービス「nly.kr」を作った話を書きましたが、今回はその裏側で実際に悩んだセキュリティ面の話をまとめてみます。
URL短縮サービスは仕組み自体はシンプルです。長いURLを受け取り、短いコードを発行し、アクセスされたら元のURLにリダイレクトする。それだけです。ただ、この「リダイレクトする」という機能そのものが、悪用されやすい性質を持っています。実装を進める中で、想定より多くの落とし穴があることに気づきました。
この記事では、実際に対応した内容を中心に、次の4つの観点でまとめます。
Open Redirect対策
登録時のURLバリデーション
Rate Limit
不正利用(Abuse)対策
Open Redirectのリスク
URL短縮サービスの本質的な機能は「任意のURLへリダイレクトすること」です。裏を返せば、これはOpen Redirect脆弱性そのものの構造でもあります。
一般的なWebアプリでのOpen Redirect対策は「許可されたドメインのみへリダイレクトする」ことですが、URL短縮サービスではそれができません。ユーザーが短縮したいのはまさに「任意の外部URL」だからです。
そのため、代わりに以下のような方向で対策しました。
生成されたリダイレクト先を、フィッシングサイトやマルウェア配布URLのブロックリストと突き合わせる
スキーム(javascript:、data: など)を厳格にホワイトリスト化し、http/https 以外は登録時点で拒否する
内部ネットワークやループバックアドレス(localhost、127.0.0.1、プライベートIPレンジなど)へのリダイレクトを禁止し、SSRFの踏み台にされることを防ぐ
リダイレクト前に中間ページを挟み、「このリンクは外部サイトnly.krを経由しています」といった表示を出すことも検討したが、UXとのバランスを見て最終的には見送った
特にSSRF対策は見落としがちな部分でした。短縮URL自体は外部ブラウザから叩かれることが前提ですが、サーバー側でメタデータ取得(OGPのタイトルや説明文を先読みするような処理)を行う場合、そのリクエスト自体が内部サーバーへの攻撃経路になり得ます。メタデータ取得処理を実装する際は、ここも同様にIPアドレスの検証が必要でした。
登録時のURLバリデーション
短縮対象のURLを受け取る時点でのバリデーションも重要です。ここで甘くすると、後段のあらゆる対策が意味をなさなくなります。
実装したチェックは主に以下の通りです。
URLとしての構文が正しいか(パースできるか)
スキームが http/https に限定されているか
ホスト名が空でないか、IPアドレス直指定の場合は上記のプライベートレンジに該当しないか
極端に長いURL(DoS目的での大量データ送信)を拒否する長さ制限
短縮対象のURLが、自サービス自身の短縮URLでないか(無限リダイレクトループの防止)
最後の「自己参照チェック」は地味ですが重要でした。悪意がなくても、ユーザーが誤って自分の短縮URLをもう一度短縮しようとするケースがあり、これを放置するとループするリダイレクトチェーンが生まれてしまいます。
Rate Limit
個人開発のサービスは、想定外の大量アクセスに弱い構成になりがちです。特にAPI経由での短縮URL発行機能を公開している場合、ここがボトルネックであり攻撃対象にもなります。
対応の方向性としては次の通りです。
IPアドレス単位でのリクエスト数制限(一定時間あたりの発行数上限)
会員登録済みユーザーと未登録ユーザーで、上限値を分ける
短時間に大量の異なるURLを登録しようとする挙動を検知し、一時的にブロックする
Rate Limitの実装自体はミドルウェアレベルで比較的簡単に組み込めますが、「どの粒度で制限するか」の設計に一番時間を使いました。IPだけで見るとNAT配下の複数ユーザーを誤ってブロックしてしまうことがあるため、IP単位の制限に加えて、ログイン済みユーザーはアカウント単位でも別途カウントするようにしています。
不正利用(Abuse)対策
Rate Limitやバリデーションをすり抜けてくる、より悪質なケースへの対応も必要でした。具体的には次のようなパターンです。
スパムやフィッシング目的での大量の短縮URL生成
短縮URLを使った迷惑メール・迷惑DMでの拡散
生成後に元のURLの内容を差し替える攻撃(今回のサービスでは元URL自体は固定なのでこのリスクは低いですが、念のため確認しました)
これらに対しては、事後対応の仕組みとして、以下を用意しました。
通報を受けたリンクを手動でブロックできる管理画面
短時間に大量生成されたリンクをまとめて無効化できる一括処理
ブロック済みリンクへのアクセス時に、適切なエラーページ(404ではなく専用の「このリンクは利用できません」ページ)を返す
事前に完璧に防ぐのは現実的に難しいので、「検知してからどれだけ早く塞げるか」を意識した設計にしています。
まとめ
URL短縮サービスは実装自体はシンプルですが、「任意の外部URLへリダイレクトする」という機能の性質上、セキュリティ面で考慮すべき点が思っていたより多くありました。特にOpen RedirectとSSRFは、機能要件と表裏一体になっているため、対策と使い勝手のバランスを取るのが難しいポイントだったと感じています。
同じように個人開発でURL短縮サービスやリダイレクト機能を実装される方の参考になれば幸いです。次回は、短縮コードの生成方法(Base62変換や衝突処理まわり)について書く予定です。
