ホスト名のバリデーションについて考えてみた

ということで、ホスト名の入力バリデーションについて

を参考に考えてみました。

ホスト名の仕様

さて、ホスト名はどのような仕様でバリデーションしたらよいでしょう?

JPNICのページ(https://www.nic.ad.jp/ja/dom/system.html)では、以下のように説明されています。

ピリオド(.)で区切られた部分は「ラベル」と呼ばれます。1つのラベルの長さは63文字以下、ドメイン名全体の長さは、ピリオドを含めて253文字以下でなければなりません。ラベルには、英字(A~Z)、数字(0~9)、ハイフン( - )が使用できます(ラベルの先頭と末尾の文字をハイフンとするのは不可)。ラベル中では大文字・小文字の区別はなく、同じ文字とみなされます。

RFCを確認してみましょう。

最大長

DNS関連のRFCであるRFC1035(DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION)によると、

2.3.4. Size limits

Various objects and parameters in the DNS have size limits. They are listed below. Some could be easily changed, others are more fundamental.

labels 63 octets or less

names 255 octets or less

TTL positive values of a signed 32 bit number.

UDP messages 512 octets or less
http://tools.ietf.org/html/rfc1035#section-2.3.4

そして、

3.1. Name space definitions

...略...

To simplify implementations, the total length of a domain name (i.e., label octets and label length octets) is restricted to 255 octets or less.
http://tools.ietf.org/html/rfc1035#section-3.1

上記のように、ドメイン名は最大255オクテット、ラベルは最大63オクテットとされています。

ただし、これはDNSメッセージ中のドメイン名(内部表現)についてであり、ドメイン名として扱える文字数は、ラベルの数よりドットの数が必ず1つ少ないこととルートドメインの空ラベル分を除いて253文字以下となります(解説は、https://www.nic.ad.jp/ja/dom/system.htmlhttp://dnsops.jp/event/20130719/20130719-dns-primer-takizawa-4.pdfなどを参照してください)。

他のRFCも確認しておきましょう。

SMTP関連のRFCであるRFC5321(Simple Mail Transfer Protocol)では、

4.5.3.1.2. Domain

The maximum total length of a domain name or number is 255 octets.
http://tools.ietf.org/html/rfc5321#section-4.5.3.1.2

上記のようにドメイン名が最大255オクテットとされています。

それから、RFC1123(Requirements for Internet Hosts -- Application and Support)では、

Host software MUST handle host names of up to 63 characters and SHOULD handle host names of up to 255 characters.
https://tools.ietf.org/html/rfc1123#page-13

255文字までを扱うべきであるとなっており、上限は明示されていません

まとめると、最大長は、

  • RFC1035 … 253オクテット
  • RFC5321 … 255オクテット
  • RFC1123 … 無制限

となります。さて、どうしたらいいのでしょう?

長さを短くすると利便性が減るという意見も見られますが、そもそも、DNSで名前解決できないほど長いホスト名を許容する必要性はあるでしょうか?

少なくとも私は253文字を超えるホスト名が必要だったケースを知りませんし、そのような必要性を想像するのも難しいです。

ちなみに、以下の長いホスト名でもまだ216文字です。

please-try-to.send-me-an-email-if-you-can-possibly-begin-to-remember-this-coz.this-is-the-longest-email-address-known-to-man-but-to-be-honest.this-is-such-a-stupidly-long-sub-domain-it-could-go-on-forever.pacraig.com

ということで、通常は253文字までを許可すればアプリケーション仕様としても問題ないと思います。また、1つのラベルは63文字までとなります。

ホスト名にはIPアドレスも入力できる

それから、ホスト名にはIPアドレスも入力できるという仕様がRFC1123にあります。

詳細は、「EximのGHOST脆弱性の影響とバリデーションの関係」への疑問の「(3) そもそもホスト名にはIPアドレスも入力できる」を参照願います。

ホスト名のバリデーションの実装

ということで、ホスト名はFQDNの場合、253文字(バイト)まで受け付けることにします。

あとは、IPアドレスが入力された場合をどうするかです。これはアプリケーション仕様でしょうが、そんなに簡単ではないですね。たぶんこれをどうするか仕様書に書いてあることは少ないのではないかと思います。

とりあえず、より汎用的に

  • IPアドレスを受け取るかエラーとするかを選択可能にする
  • 入力が数字とドットのみの場合は、IPアドレスと判断する
    • 何故なら、そのようなドメイン名は現在存在しないから
  • IPアドレスを受け取る場合
    • 入力が数字とドットのみの場合、IPv4としての形式をチェックする
  • IPアドレスを受け取らない場合
    • 入力が数字とドットのみの場合、エラーとする

として、ホスト名をバリデーションする関数をPHPで書いてみました。なお、国際化ドメイン名およびIPv6アドレスについては考慮していません。

<?php

/**
 * Validate Hostname (FQDN)
 * 
 * @param string $hostname  FQDN or IPv4 address
 * @param bool   $accept_ip Wheather IPv4 address is accepted or not
 * @return boolean
 */
function validate_host($hostname, $accept_ip = false)
{
    if (! is_string($hostname)) {
        return false;
    }

    $len = strlen($hostname);

    // total lenght is not more than 253 bytes
    if ($len > 253) {
        return false;
    }

    $labels = explode('.', $hostname);

    // needs at least one dot
    if (count($labels) < 2) {
        return false;
    }

    $is_ipv4 = true;
    $alnum = implode('', range('0', '9')) . implode('', range('a', 'z'))
           . implode('', range('A', 'Z'));

    foreach($labels as $label) {
        $label_len = strlen($label);

        // label lenght is not more than 63
        if (! $label_len || $label_len > 63) {
            return false;
        }

        // the first byte and the last byte are not hyphens
        if (substr($label, 0, 1) === '-' || substr($label, -1, 1) === '-') {
            return false;
        }

        // allowed chars are alnum and hyphens
        if ($label_len !== strspn($label, $alnum . '-')) {
            return false;
        }

        // check if all chars are digits only
        if (! ctype_digit($label)) {
            $is_ipv4 = false;
        }
    }

    // in case all labels are made of digits only, that is IPv4 address.
    if ($is_ipv4) {
        if ($accept_ip) {
            // total lenght is not more than 15 bytes
            if ($len > 15) {
                return false;
            }

            // check IPv4 address format if you accept IPv4 address
            if (filter_var($hostname, FILTER_VALIDATE_IP) === false) {
                return false;
            }
        } else {
            // invalidate if you don't accecpt IPv4 address
            return false;
        }
    }

    return true;
}

仮にこのような実装のバリデーションを実施していれば、EximのGHOST脆弱性も防げたと思います。

ちなみに、上記のコードが長すぎると感じた方には、「Zend Framework 2のホスト名のバリデータ」を参照されることをお薦めします。

まとめ

まとめというより感想ですが。

  • 入力バリデーションは実際にどう実装するかが最大の問題
  • アプリケーション仕様ははっきりしない場合も多く、バリデーションが甘い場合も多いと思われる
  • どこまで厳格にバリデーションするか、どのように判断したらよいのか?
    • ホスト名の最大長は253文字でよいと思う

参考

Tags: security, php, validation

JNSAセキュアシステム開発ガイドラインの入力バリデーション

2005年に出された古いガイドラインですが、「JNSAセキュアシステム開発ガイドライン「Web システム セキュリティ要求仕様(RFP)」編β版」に以下の記載例がありました。


(以下引用)

1.入力検証および不正データ入力時の無効化

ユーザが悪意のある文字列を組み込んでアプリケーションを攻撃し、本来権限のないユーザがデータにアクセス(情報の入手、情報の改ざんなど)できないように、以下を考慮した対策を提案すること。

  • 悪意ある文字列の入力チェックもしくは無害化
  • SQLインジェクションの防御
  • コマンドインジェクションの防御
  • パストラバーサルの防御
  • パラメータ改ざんの防御
  • クロスサイトスクリプティングの防御
  • バッファオーバフローの防御

(引用ここまで)


これ、「入力検証および不正データ入力時の無効化」の見出しを削除すればいいと思いますが、素直に読むと列挙されている項目を全部入力時に防げということですよね。

このガイドライン、改訂されないんでしょうかね?

ところで、バッファオーバフローの防御はどうしたらいいんでしょう?ご存知の方がいらっしゃいましたら、お教えください。

Tags: security, validation

永きに渡るバリデーション論争に終止符を打つために

記録よると最初のバリデーション論争が起こったのは2008年だそうです。

その後、たびたび論争が起き現在に至ると言うのが人類の歴史です。

この論争はいろいろなトピックを含む興味深いもの(とどうでもよいものの集合)ですが、一言で表すとすると、以下の問いに集約できるのではないかと思います。

入力バリデーションはセキュリティ対策かどうか?

そして、そろそろこの論争にも終止符を打てないかと思います。なんとなく疲れてきている人が多そうなためです(w

そのために何が一番必要かと考えると、言葉や概念の整理、定義がやっぱり必要なのではないかと思いました。

概念の整理

アプリケーション仕様とセキュリティ仕様

私の理解では、「アプリケーション仕様」の中に「セキュリティ仕様」があります。アプリケーション仕様の方が広い概念であり、セキュリティ仕様はその一部になります。

アプリケーション仕様⊃セキュリティ仕様

そして、セキュリティ仕様を実装したものが「セキュリティ対策」です。

ただし、これだけでは、セキュリティ対策が何なのかはわかりません。

セキュリティ対策とは何か?

日本語として普通に考えると、セキュリティ対策は「セキュリティのための対策」なのかなと思います。でも、まだ漠然としています。

また、主な議論の対象であるWebアプリケーションセキュリティやインターネットセキュリティという分野において、「セキュリティ対策」の公式な定義はないようです。

ということで、漠然とした概念が議論をややこしくしているようです。

twitter上での私の情報収集によれば、この議論でのセキュリティ対策の定義には大きくわけて以下の2つがあるようです。

  1. 結果としてリスクを緩和するすべての対策
  2. リスク緩和を主目的として実施する対策

ここでは、1.を「広義のセキュリティ対策」、2.を「狭義のセキュリティ対策」と呼ぶことにします。

入力バリデーションはセキュリティ対策なのか?

「広義のセキュリティ対策」を採用すれば、入力バリデーションはリスクを緩和することも多々あるので「入力バリデーションはセキュリティ対策である」となるでしょう。

「狭義のセキュリティ対策」を採用すれば、入力バリデーションはリスク緩和を主目的とはしていないでしょうから「入力バリデーションはセキュリティ対策ではない」となるでしょう。

つまり、入力バリデーションはセキュリティ対策なのか?という問いの答えは「はい」でも「いいえ」でもどちらも正しいと言えます。

あるいは、言い換えれば、「入力バリデーションはセキュリティ対策なのか?」は「セキュリティ対策が何なのか」という前提条件により結果は変わります。

われわれは何を議論しているのか?

入力バリデーションはセキュリティ対策なのか?についてはどちらもと言える、という結論に至りました。

そして新たな問い、あるいは真の問いが生じます。

入力バリデーションがセキュリティ対策か否かを区別する必要性は何か?

あるいは、

入力バリデーションはセキュリティ対策であるべきか?

念のため記載しておきますが、残念ながら入力バリデーションをしなくてもいいという意見の方はいないようです。入力バリデーションがセキュリティ対策か否かにかかわらず、入力バリデーションは実施すべきとされています。


以下は徳丸さんの日記より引用 http://blog.tokumaru.org/2015/02/eximghost.html

では、バリデーションはどのように考えればよいでしょうか。従来から言っていることですが以下を推奨します。

  • バリデーションの基準はアプリケーションの仕様である
  • 仕様を満たさない入力値は再入力を促すナビゲーションを行う
  • アプリケーション仕様として、すべての入力パラメータの以下項目を定義しておく
    • 文字種
    • 文字列長の最小・最大値
    • 数値の場合は最小値・最大値
    • メールアドレス等は書式

文字列長に関しては、制限をとくに設けていないアプリケーションも多いと思いますが、実用性を損なわない範囲で上限値を定めて、その上限まで正常に動作することと、上限値を超えた場合にエラーになることをテスト項目に加えるとよいでしょう。

(引用ここまで)


もし、入力バリデーションを省略してもいいという意見の方がいればお教えください。

関連

Tags: security, validation