はじめに 「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.TryCreateにUriKind.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 をちゃんと受け取って中身を見る
多くの業務システムでは、
httpとhttpsだけ許可したいftpやfile、javascriptなどは弾きたい
という要件が出てきます。
その場合は、Uri.TryCreate で Uri オブジェクトを作ってから、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.Schemeでhttp/httpsなどを判定し、危険なスキームを弾くuri.HostやEndsWith(".example.com")で、許可するドメインを絞り込める- 正規表現は「URLっぽいかどうか」の軽い前処理にとどめ、最終判定は
Uriに任せる - 意図ごとにメソッドを分けたユーティリティにしておくと、呼び出し側のコードが読みやすくなる
ここまで理解できれば、「なんとなくURLっぽいか見ている」段階から一歩進んで、
“安全で現実的なURL検証ユーティリティ”を、自分の手で設計・実装できるようになっていきます。
