C# Tips | 文字列処理:URL検証

C# C#
スポンサーリンク

はじめに 「URL検証」は“危なそうな文字列を入口で止めるフィルタ”

業務システムで URL を扱う場面は多いですよね。

  • ユーザーが入力したホームページURL
  • 外部サービスのコールバックURL
  • 管理画面で設定するAPIエンドポイント

ここで大事なのは、

「それ、本当に“URLとしてまとも”か?」

を、入口でちゃんとチェックしておくことです。

ただし、メールアドレスと同じで、
URLも仕様を厳密に追い始めると沼にハマります。
実務では、

  • 「最低限の形式(スキーム・ホストなど)が正しいか」
  • 「許可していないスキーム(javascript: など)が紛れ込んでいないか」

を押さえることが重要です。

ここでは、初心者向けに、

  • Uri.TryCreate を使った“王道のURL検証”
  • スキーム(http / https など)の制限
  • 正規表現を使うべきかどうか
  • 実務で使いやすいユーティリティ化の形

を、例題付きでかみ砕いて説明していきます。


URL検証の基本方針 「正規表現より Uri クラスを信じる」

正規表現でURLを全部判定しようとしない

まず大事なことを先に言うと、

URLの妥当性チェックを正規表現だけでやろうとしない方がいい

です。

理由はシンプルで、

  • URLの仕様はかなり複雑
  • 国際ドメイン、IPv6、ポート、クエリ、フラグメント…全部を正規表現で書くと地獄
  • .NET には Uri という、URLをちゃんと解析してくれるクラスがある

からです。

なので、基本は Uri クラスに任せる
そのうえで、「自分たちのルール(http/httpsだけ許可など)」を上乗せする、という考え方が現実的です。


Uri.TryCreate を使った“王道のURL検証”

一番シンプルな「URLとして妥当か?」チェック

まずは、「文字列がURLとして構文的に妥当か?」をチェックする基本形です。

using System;

public static class UrlValidator
{
    public static bool IsValidUrl(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return false;
        }

        return Uri.TryCreate(value.Trim(), UriKind.Absolute, out _);
    }
}
C#

ここでのポイントはこうです。

  • null や空白だけなら false
  • Uri.TryCreateUriKind.Absolute を指定して、「絶対URLかどうか」をチェック
  • 成功したかどうかだけ見たいので、out _ で結果の Uri は捨てている

動作例

Console.WriteLine(UrlValidator.IsValidUrl("https://example.com"));          // true
Console.WriteLine(UrlValidator.IsValidUrl("http://example.com/path"));      // true
Console.WriteLine(UrlValidator.IsValidUrl("ftp://example.com/file.txt"));   // true(この時点ではOK)
Console.WriteLine(UrlValidator.IsValidUrl("example.com"));                  // false(スキームなし)
Console.WriteLine(UrlValidator.IsValidUrl("not a url"));                    // false
Console.WriteLine(UrlValidator.IsValidUrl(""));                             // false
C#

ここまでで、「とりあえずURLとして構文的におかしくないか?」は判定できるようになります。


スキームを制限する 「http/httpsだけ許可」などのルール

Uri をちゃんと受け取って中身を見る

多くの業務システムでは、

  • httphttps だけ許可したい
  • ftpfilejavascript などは弾きたい

という要件が出てきます。

その場合は、Uri.TryCreateUri オブジェクトを作ってから、
Scheme プロパティをチェックします。

using System;

public static class UrlValidator
{
    public static bool IsValidHttpUrl(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return false;
        }

        if (!Uri.TryCreate(value.Trim(), UriKind.Absolute, out var uri))
        {
            return false;
        }

        // スキームを http / https に限定
        return uri.Scheme == Uri.UriSchemeHttp ||
               uri.Scheme == Uri.UriSchemeHttps;
    }
}
C#

動作例

Console.WriteLine(UrlValidator.IsValidHttpUrl("https://example.com"));      // true
Console.WriteLine(UrlValidator.IsValidHttpUrl("http://example.com/path"));  // true
Console.WriteLine(UrlValidator.IsValidHttpUrl("ftp://example.com/file"));   // false
Console.WriteLine(UrlValidator.IsValidHttpUrl("javascript:alert('xss')"));  // false
Console.WriteLine(UrlValidator.IsValidHttpUrl("example.com"));              // false
C#

ここまでやると、

  • 「URLとして構文的に妥当」かつ
  • 「スキームが http/https のどちらか」

という条件を満たしたものだけを通せるようになります。


ホスト名やポートなど、もう少し踏み込んだチェック

ホストが存在するかどうか(空でないか)

Uri オブジェクトからは、ホスト名やポートなども取得できます。

public static bool IsValidHttpUrlWithHost(string? value)
{
    if (string.IsNullOrWhiteSpace(value))
    {
        return false;
    }

    if (!Uri.TryCreate(value.Trim(), UriKind.Absolute, out var uri))
    {
        return false;
    }

    if (uri.Scheme != Uri.UriSchemeHttp &&
        uri.Scheme != Uri.UriSchemeHttps)
    {
        return false;
    }

    // ホストが空でないことを確認
    if (string.IsNullOrEmpty(uri.Host))
    {
        return false;
    }

    return true;
}
C#

例えば、"http://" のような文字列は Uri.TryCreate 的には作れてしまうことがありますが、
ホストが空なので、ここで弾くことができます。

特定ドメインだけ許可したい場合

「自社ドメイン配下のURLだけ許可したい」といった要件もよくあります。

public static bool IsValidCompanyUrl(string? value, string allowedHostSuffix)
{
    if (string.IsNullOrWhiteSpace(value))
    {
        return false;
    }

    if (!Uri.TryCreate(value.Trim(), UriKind.Absolute, out var uri))
    {
        return false;
    }

    if (uri.Scheme != Uri.UriSchemeHttp &&
        uri.Scheme != Uri.UriSchemeHttps)
    {
        return false;
    }

    if (string.IsNullOrEmpty(uri.Host))
    {
        return false;
    }

    // 例: allowedHostSuffix = ".example.com"
    return uri.Host.EndsWith(allowedHostSuffix, StringComparison.OrdinalIgnoreCase);
}
C#

例:

Console.WriteLine(IsValidCompanyUrl("https://service.example.com", ".example.com")); // true
Console.WriteLine(IsValidCompanyUrl("https://evil.com", ".example.com"));            // false
C#

こうしておくと、「URLとして妥当」かつ「自社ドメイン配下」という条件を満たしたものだけを通せます。


正規表現でURLをチェックしたくなったときの話

「本気のURL正規表現」はやめておいた方がいい

インターネット上には、ものすごく長い「URL用正規表現」がたくさんあります。
ですが、初心者のうちは、そして業務システムでは、それをコピペして使うのはおすすめしません

理由は、

  • 何を許して何を禁止しているのか、読んでも分からない
  • 仕様変更(たとえば新しいTLDなど)に追従しづらい
  • Uri クラスという、すでにある強力なパーサを捨てることになる

からです。

「どうしても正規表現を使いたい」なら“軽い前処理”にとどめる

どうしても正規表現を使いたい場合は、

  • 「スキームっぽいものが先頭にあるか?」
  • 「空白を含んでいないか?」

などの“軽い前処理”にとどめて、
最終的な判定は Uri.TryCreate に任せる、という使い方が現実的です。

例:

using System.Text.RegularExpressions;

private static readonly Regex SimpleUrlPattern =
    new Regex(@"^[a-zA-Z][a-zA-Z0-9+\-.]*://\S+$", RegexOptions.Compiled);

public static bool IsProbablyUrl(string? value)
{
    if (string.IsNullOrWhiteSpace(value))
    {
        return false;
    }

    return SimpleUrlPattern.IsMatch(value.Trim());
}
C#

これはあくまで「URLっぽい形かどうか」のざっくりチェックで、
本当の検証は Uri に任せる、というスタンスです。


実務で使いやすい「URL検証ユーティリティ」の形

役割を分けたメソッドにしておく

実務で長く使うことを考えると、
「何をチェックしているか」がメソッド名から分かるようにしておくのが大事です。

例えば、こんな分け方が考えられます。

public static class UrlValidationUtil
{
    // 1. とにかく絶対URLとして妥当か?
    public static bool IsAbsoluteUrl(string? value) { ... }

    // 2. http/https の絶対URLか?
    public static bool IsHttpUrl(string? value) { ... }

    // 3. 自社ドメイン配下の http/https URL か?
    public static bool IsCompanyHttpUrl(string? value, string allowedHostSuffix) { ... }
}
C#

呼び出し側は、

  • 「ここは http/https だけ許可したいから IsHttpUrl
  • 「ここは自社ドメインだけ許可したいから IsCompanyHttpUrl

というふうに、意図に合わせてメソッドを選べるようになります。

null や空文字の扱いをユーティリティ側で決めておく

どのメソッドも、

  • null や空文字は false
  • Trim() してから判定

というルールにしておくと、呼び出し側で余計な if が減ります。

「必須かどうか(空を許すか)」は、
URL検証ユーティリティの外側(画面やバリデーションレイヤー)で決めるのがきれいです。


まとめ 「URL検証ユーティリティ」は“Uriに任せて、自分たちのルールを薄く足す”

URL検証は、正規表現で頑張るよりも、

  • 構文の解釈は Uri クラスに任せる
  • スキームやホストの制限など“業務ルール”だけを自分で足す

というスタイルが、シンプルで壊れにくく、初心者にも扱いやすいです。

押さえておきたいポイントは、

  • Uri.TryCreate(..., UriKind.Absolute, out uri) がURL検証の基本形
  • uri.Schemehttp / https などを判定し、危険なスキームを弾く
  • uri.HostEndsWith(".example.com") で、許可するドメインを絞り込める
  • 正規表現は「URLっぽいかどうか」の軽い前処理にとどめ、最終判定は Uri に任せる
  • 意図ごとにメソッドを分けたユーティリティにしておくと、呼び出し側のコードが読みやすくなる

ここまで理解できれば、「なんとなくURLっぽいか見ている」段階から一歩進んで、
“安全で現実的なURL検証ユーティリティ”を、自分の手で設計・実装できるようになっていきます。

タイトルとURLをコピーしました