PythonでURLを安全に検証する
Afzaal Ahmad Zeeshan
2022年11月4日
0 分で読めますインターネット上のすべてのものには、それぞれを一意に識別するUniform Resource Locator(URL)があり、インターネットユーザーはこれを使ってファイルやその他のメディアにアクセスできます。たとえば、この記事にも固有のURLがあり、検索エンジン最適化(SEO)クローラーがインデックスに登録することで、ユーザーが記事を見つけられるようになります。
URLの構文に関する最初の定義は、1994年のRequest for Comments (RFC)1738に記載されています。それ以来、URLの構造はセキュリティ向上のために何度も改訂されてきました。しかし、開発者がRFCの定義を本来の意図どおりに使わないことも多く、さまざまな悪意ある攻撃の一因となっています。
最近の例として、広く使われているJavaのロギングパッケージLog4jで見つかったRCEのゼロデイエクスプロイトがあります。この攻撃は、Java Naming and Directory Interface(JNDI)が悪意あるログ文字列を評価した際に発生しました。JNDIは、Javaソフトウェアクライアントが名前を使ってデータやリソース(Javaオブジェクトの形式)を検出、検索できるようにする、ディレクトリサービス用のJava APIです。悪意あるログ文字列を評価すると、リモートサーバーに接続し、悪意のあるJavaコードを実行します。そのため、企業は顧客やパートナーなど外部の関係者から提供されるURLを常に検証する必要があります。
もう一つの例はサーバーサイドリクエストフォージェリ攻撃です。安全でないURLにアクセスすると、サーバープログラムが侵害される可能性があります。悪意のあるユーザーは入力欄を悪用して、Webソリューションに損害を与えるだけでなく、組織の対外的な評判を損なうおそれもあります。
この記事では、不正なURLがもたらす課題やアプリケーションへの被害、その対処方法について解説します。一緒に試すには、お使いのマシンにPythonをインストールしてセットアップしてください。
URLに潜むセキュリティリスク
URLは、ユーザーを正規のWebページから悪意のあるページへ誘導する可能性があるため、リスクを伴います。また、クロスサイトスクリプティング(XSS)など、複数の攻撃経路を生み出すこともあります。XSSは一部のWebアプリケーションに存在するセキュリティ上の脆弱性で、攻撃者が他のユーザーの閲覧するWebページにクライアント側スクリプトを挿入できるようにします。
このセキュリティリスクは、銀行業界など特定の業界で深刻な結果を招く可能性があります。顧客が銀行アプリケーションのログインページだと思い込み、攻撃者が管理する本物そっくりのページを開いてしまうことがあります。これはソーシャルエンジニアリングを利用したフィッシング攻撃の一種です。攻撃者は、相手をだまして機密情報を明かさせるために、偽造または誤解を招くメッセージを送信します。また、被害者のインフラにランサムウェアなどの悪意あるソフトウェアを展開することもあります。攻撃者が銀行ユーザーのユーザー名とパスワードを入手すれば、その情報を使って口座にログインし、被害を与えることができます。
URLを悪用したさらに高度な攻撃は、サーバーサイドリクエストフォージェリによって発生することがあります。この攻撃は、顧客が指定したURLにリクエストを送信するサーバーを標的にします。リクエストの送信時に攻撃者がサーバーを乗っ取り、ポートやネットワーク情報のスキャン、インフラのメタデータの要求、印刷など、許可されていない操作を実行したり、パスワードやトークンなどの機密情報を出力させたりする可能性があります。
以降のセクションでは、管理できないURLを検証、サニタイズする方法を紹介します。たとえば、ユーザーがコミュニケーションやデータ共有を行うソーシャルプラットフォームを構築する場合、許可するURLと非表示にするURLを管理できます。
PythonでURLを検証する方法
プログラミング言語のPythonは、世界中の何百万もの顧客向けのWebアプリケーションやサイトの構築に広く使われています。PythonにはURLスキャナーやバリデーターが組み込まれていませんが、コミュニティ主導のURLパーサーやバリデーターが利用できます。これらを使ってURLを検証し、安全性を高められます。また、一部のWebアプリケーションフレームワークには、Webアプリケーションの開発に使えるURLスキャナーやバリデーターが用意されています。
URLの検証
Pythonで広く使われているURL検証方法の一つに、Pythonパッケージのvalidatorsがあります。validatorsパッケージを使って、URLが有効かどうかを判定できます。有効なURLは、次の条件を満たす必要があります。
HTTPまたはHTTPSの仕様に関するすべてのルールに従い、正しい形式であること
そのアドレスにリソースが存在すること。関連するリソースがなければ、URLは無効です
Pythonのvalidatorsパッケージには、URLを検証し、安全性を確認して、無効なキーワードや文字を検索するURLメソッドが用意されています。URLがパブリックドメインで利用できる場合(ファイアウォールや有料アクセスなどの障壁で保護されていない場合)、内部IPアドレスは対象から除外されます。
validatorsパッケージを使うには、pipを使ってローカルのPython環境に依存関係をダウンロードし、セットアップします。パッケージをダウンロードするには、ローカルのターミナルまたはコマンドラインインターフェイスで次のコードを実行します。
このコマンドが完了すると、マシンにvalidatorsパッケージがインストールされます。
次に、main.pyという名前のPythonファイルを作成し、URLをテストするPythonコードを記述します。
このコードをmain.pyファイルに記述したら、PythonのコマンドラインインターフェイスからPythonインタープリターで実行します。
URLのhttp:/の後に/が欠けているため、コードは"URL is invalid"と出力します。
このパッケージは、URLがパブリックにアクセス可能かどうかも判定できます。ユーザーが内部IPアドレスへのリクエストを試みていないか確認する際に便利です。同じPythonファイルに次のコードを追加して実行し、URLを検証してみましょう。
今回も、URLは無効という結果が出力されます。URL自体に問題がなくても、パブリックドメインからアクセスできないためです。validatorsパッケージの機能について詳しくは、ドキュメントを参照してください。
正規表現を使った解析
URLを検証するもう一つの方法は、正規表現を使うことです。たとえば、検証するURLにHTTPSが含まれていることを必須にできます。この検証を行うコードは次のとおりです。
この正規表現はhttps://www.google.comには一致しますが、http://www.google.comには一致しません。どちらも有効なURLです。正規表現について詳しくは、こちらのWebサイトをご覧ください。上記の式をPythonコードで試してみましょう。
上記のコードを実行すると、一致したURLが出力されます。上記のURL文字列からHTTPSを削除するか、HTTPに変更すると、Noneオブジェクトが返されます。これは、一致するURLがなかったことを示します。
しかし、正規表現は複雑で、実際の用途には適していません。正規表現は読み解きにくく、デバッグやスケーリングも困難です。そのため、一般的にはライブラリを使う方が適しています。
準拠する構造と構文をすべて使ってURLを解析する、より高度な正規表現の例については、Stack Overflowのスレッドをご覧ください。
正規表現を使う利点の一つは、入力文字列に含まれる無効なURLも見つけられることです。一般的なライブラリではできません。validatorsパッケージは、文字列の前後に空白がある場合でも正しく動作しません。パッケージを適切に使うには、入力文字列をサニタイズしてから、validatorパッケージに渡す必要があります。
urllibを使う
URLを解析してその各要素を取り出す別のパッケージに、urllibがあります。Python 3インタープリターで使用できます。
次のコードでURLが有効かどうかを確認できます。
result変数のスキームとnetlocフィールドが設定されていれば、URLは有効で使用できます。そうでなければURLは無効なので、注意が必要です。
よくある脆弱性を見つける
DjangoなどのPythonフレームワークには、フレームワーク内のURLを検証できる組み込みのバリデーターパッケージがあります。しかし、こうしたライブラリに依存する場合、フレームワークに精通していても、パッケージ自体の安全性を確認する必要があります。また、オープンソースであることだけでは、パッケージを信頼できるかどうかの判断がさらに難しくなります。
Snyk Advisorのようなセキュリティツールを使えば、現在利用しているすべてのオープンソースパッケージや、新たに導入したいパッケージに、よくある脆弱性がないかをすばやく確認できます。このレビューに基づいてSnykが提供するセキュリティレポートを使い、パッケージを採用すべきかどうか判断できます。たとえば、上記のデモで使用したvalidatorsパッケージなどが安全かどうかを、Snyk Advisorで確認できます。
また、Snyk Open SourceをSnyk Advisorと併用すれば、オープンソースのツールスタックに潜むライセンス上の問題や脆弱性など、セキュリティに関する懸念を発見できます。さらに、Snyk Vulnerability Database(VulnDB)でURL関連の既知の脆弱性を検索できます。たとえば、URLを検証せずにユーザーを別の場所へリダイレクトするFlaskフレームワークのこの脆弱性が挙げられます。このデータベースを活用することで、Webページやアプリケーションを先回りして安全にできます。
PythonでURLを検証する
この記事では、サニタイズされていないURLがWebアプリケーションにもたらす課題と、必要に応じてURLをサニタイズする方法を解説しました。まず、validatorパッケージとurllibパッケージを使った基本的なURL検証を行いました。次に、これらのパッケージを使い、URLがすべてパブリックなものか、Webアプリケーションへの攻撃に使われる内部URLが含まれているかを確認する方法を紹介しました。その後、基本的な要件としてすべてのURLがHTTPSであることなどを、簡単な1行のコードでスキャンする正規表現の例を示しました。また、正規表現によるURL検証が複雑で難しい理由についても説明しました。
最後に、安全なオープンソースライブラリを選ぶ方法について解説しました。Snyk Advisorは、オープンソースパッケージを利用する際の信頼できる情報源です。詳しくはPythonセキュリティのベストプラクティス・チートシートを確認し、継続的インテグレーション/継続的デリバリー(CI/CD)パイプラインで、コードに追加される脆弱性を確認できるようにしましょう。
CTFを始めよう
オンデマンドのバーチャル入門ワークショップを視聴して、CTFの課題の解き方を学びましょう。



