先週の記事で、内部ドメイン・外部ドメインの切り分けについて記載いたしました。
その際にサンプルには、存在しないドメインをエラーとする機能をつけていました。
誰でも入力できる入力欄には、様々なリスクが存在しています。
システムを守るためには、様々なセキュリティチェックが必要です。
その一つとして、ドメイン存在チェックがあります。
例えばデータベースと連携するような場合には、より綿密なチェックが必要です。
なぜならば、それがシステムを安全に稼働させることにつながるからです。
今回は、ドメインが存在確認チェックについて記載いたします。
ドメインチェックするサンプル
論より証拠です。
ドメインの存在チェックするサンプルはこちらです。
このサンプルは、ドメインの書式チェックだけでなく、存在するかどうかもチェックします。
書式が正しいのにはエラーになるのはそのためです。
ソースはこちらです
<?php
// 初期値設定
$input_url = "office-obata.com";
$output_url = null;
if ((isset($_REQUEST["url"]) == true) /* URLが送信された ? */
&& (isset($_REQUEST["reset"]) != true)) /* リセットボタンでない ? */
{
/** URL取得 */
$input_url = $_REQUEST["url"];
/** メッセージ初期化 */
$msg = "正しくありません。";
$class = " class='err'";
/** ドメインチェック */
$sts = url_chk($input_url);
if ($sts["sts"] == true) /* 正しいドメイン ? */
{
/** メッセージセット */
$msg = "正しいドメインです。";
$class = null;
}
/** ドメインセット */
$domain = $sts["domain"];
/** メッセージセット */
$output_url = "<div${class}><p>入力されたドメインは${msg}</p><p>${domain}</p></div>";
}
function url_chk($url)
{
/** ステータス初期化 */
$sts = array("sts"=>false, "domain"=>null);
/** URL取得(https://の記述をとる) */
$domain = str_replace(array("https://", "http://"), null, $url);
if (strpos($domain, "/") == true) /* 下層ページURL ? */
{
/** ドメインだけを抽出する */
$domain = substr( $domain, 0, strpos( $domain, "/" ));
}
/** ドメインセット */
$sts["domain"] = $domain;
if ((filter_var("https://${domain}", FILTER_VALIDATE_URL) != true) /* ドメインの書式がNG ? */
|| (strpos($domain, ".") === false)) /* ドットが含まれていない ? */
{
/** 処理破棄 */
return $sts;
}
if ((checkdnsrr($domain, "A") === true)
|| (checkdnsrr($domain, "MX") === true)) /* 正しいドメイン ? */
{
/** ステータスOKセット */
$sts["sts"] = true;
}
/** 処理終了 */
return $sts;
}
?>
<!doctype html>
<html>
<head>
<meta charset="shift_jis">
<title>外部リンク判定</title>
<style type="text/css">
section
{
text-align: center;
width: 600px;
margin-right: auto;
margin-left: auto;
padding-top: 20px;
padding-bottom: 20px;
border: 1px solid #ddd;
border-radius: 6px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
}
div
{
padding: 10px;
width: 80%;
border: 10px solid #0C0;
color: #0C0;
margin-right: auto;
margin-left: auto;
margin-top: 10px;
}
.err
{
border: 10px solid #F00;
color: #F00;
text-align: left;
}
</style>
</head>
<body>
<section>
<form action="" method="post" enctype="multipart/form-data">
<h1>ドメイン判定チェック</h1>
<p>※ドメインが正しいかどうかをチェックします。<br>(日本語ドメインには対応していません)</p>
<input name="url" type="text" value="<?php echo $input_url; ?>" style="width: 400px">
<input name="submit" type="submit" value="送信">
<input name="reset" type="submit" value="リセット">
<?php echo $output_url; ?>
</form>
</section>
</body>
</html>
疑問:PHPでドメインの存在チェックってどうやるの?
文法チェックならわかりますが、存在チェックってどうやるんだろう?
素朴に疑問に思いました。
なぜならば、ドメインの存在チェックにはDNSのチェックが必要だからです。
PHPでそんなことができるのでしょうか?
PHPでドメイン存在チェックのやり方
PHPにはDNSの情報を確認する関数があります。
今回はcheckdnsrr()関数を使用しました。
ここでDNSについて簡単に説明いたします。
ホームページは通常ドメイン(office-obata.comなど)を入力すると表示されます。
ホームページは、一般的にはwebサーバーと呼ばれる集合住宅に格納されています。
ところが、webサーバーにアクセスるためにはIPアドレスでなければいけません。(192.999.999.999 といった数字の羅列)
これを何とかしているのがDNSです。
DNSが文字列であるドメイン(office-obata.comなど)をIPアドレス(192.999.999.999 といった数字の羅列)に変換しているのです。
つまり存在するドメイン名はDNSに記述がされています
存在するドメインは必ずDNSに記述があります。
DNSに対して、記述があるかどうかを調べれば、存在しているかどうかがわかるのです。
checkdnsrr()について
checkdnsrr()関数は、指定したドメインがdnsに存在しているかどうかをチェックする関数です。
以下のように記述します。
if ((checkdnsrr($domain, "A") == true)
|| (checkdnsrr($domain, "MX") == true)) /* 正しいドメイン ? */
{
/** ステータスOKセット */
$sts["sts"] = true;
}
引数は以下の2つです。
- ドメイン名
- DNSタイプ
ドメイン名はその名の通り、ドメイン名を入れます。
注意点は「https://」などをつけないことです。
これらがついていると、正しく動作しません。
DNSタイプとは、DNSの記述コードタイプになります。
一見よくわかりませんが、DNSに記述する場合大抵MX、A等で記述します。
上記の例だと、Aタイプで記述されているものと、MXタイプで記述されているものの中に該当ドメインが存在しているかを調べています。
ちなみに、タイプは複数同時に入れられないので、複数調べる場合はcheckdnsrr()関数を2回コールします。
存在する場合は:true
存在しない場合は:false
が帰ります。
ここでふと思いました。
DNSに存在していればOKじゃないか?
と思いがちですが、実はそうとも言えません。
なぜならば、DNSに記述されていればなんでもOKになるからです。
例えばDNSに記述されるのはなにもドメインだけではありません。
ドメイン以外のキーワードも設定されることがあります。
現に「test」という文字で試してみたらOKになりました。
ドットも拡張子もないのにOKとなりました。
これは、DNSに「test」という文字が記載されているためです。
これでは具合が悪いと思い、複合チェックにしたほうが良いと考えました。
でもどうすればよいのでしょうか?
書式チェックをいれれば解決します
「DNS存在チェック」+「ドメイン書式チェック」を行えば、互いの弱点を補えます。
- DNS存在チェックの弱点・・ドメインでない文字列もOKになることがある
- ドメイン書式チェックの弱点・・存在しない名称でもOKになることがある
この初期チェックをするのがfilter_var()関数です。
この関数は、ドメインやメールなどの書式チェックをしてくれます。
ただ、使ってみるとドット(.)がなくてもOKになったので、ドットの有り無しだけは補完します。
if ((filter_var("https://${domain}", FILTER_VALIDATE_URL) != true) /* ドメインの書式がNG ? */
|| (strpos($domain, ".") === false)) /* ドットが含まれていない ? */
{
/** 処理破棄 */
return $sts;
}
filter_var関数で書式のチェックをし、strpos関数でドットの有り無しをチェックしています。
これで「test」といったドメインでない形式にも対応できます。
このやり方には落とし穴もあるので注意が必要です。
filter_var()関数もcheckdnsrr()関数も対応していないケースが一部あります。
例えば「日本語ドメイン」などです。
ただドメインをチェックするケースを想定すると、シビアな場合が想定されます。
例えば「収集」をするようなケースです。
ホームページ一覧表を作成するようなケースでは、ここまでのチェックはいりません。
あくまでも特殊な事例で使用するケースといえます。
そのためか、ググってもドメインチェックについてはあまり見つけられませんでした。
少なくとも、不正投稿をガードするという目的では使用できる代物です。
本記事がお役に立てば幸いです。