Upgrade to Pro — share decks privately, control downloads, hide ads and more …

FQDN(ドメイン名)のバリデーションが意外と面倒だった #phpcon2022

akase244
September 25, 2022

FQDN(ドメイン名)のバリデーションが意外と面倒だった #phpcon2022

PHPカンファレンス2022の「大LT大会」のセッションで発表したスライドです。

akase244

September 25, 2022
Tweet

More Decks by akase244

Other Decks in Programming

Transcript

  1. FQDN(ドメイン名)のバリ
    デーションが意外と面倒
    だった
    PHP Conference Japan 2022
    September 24 - 25, 2022.

    View Slide

  2. @akase244
    var_dump(
    (new Me())
    ->WebApplicationEngineer()
    ->InfrastructureEngineer()
    );
    2

    View Slide

  3. なぜドメイン名の
    バリデーションをやりたかったのか?
    3

    View Slide

  4. ドメイン名の入力
    4

    View Slide

  5. こんな入力を防ぎたい
    5
    ● www.example,com 「カンマ」を含んでいる
    ● www.example com 「空白」を含んでいる
    ● www.examp!e.com 「エクスクラメーション」を含んでいる
    ● www.example.com 「ドメイン名」が含まれていない

    View Slide

  6. filter_varを発見
    6

    View Slide

  7. 7
    ● FILTER_VALIDATE_BOOL (= FILTER_VALIDATE_BOOLEAN)
    ● FILTER_VALIDATE_DOMAIN
    ● FILTER_VALIDATE_EMAIL
    ● FILTER_VALIDATE_FLOAT
    ● FILTER_VALIDATE_INT
    ● FILTER_VALIDATE_IP
    ● FILTER_VALIDATE_MAC
    ● FILTER_VALIDATE_REGEXP
    ● FILTER_VALIDATE_URL
    検証フィルタ

    View Slide

  8. FILTER_VALIDATE_DOMAIN
    にそれっぽい説明
    8

    View Slide

  9. 9
    試してみた

    View Slide

  10. 「カンマ」が含まれている
    10
    $ docker container run \
    --rm -it php:8.1.10-cli-alpine \
    php -r 'var_dump(
    filter_var(
    "www.example,com",
    FILTER_VALIDATE_DOMAIN,
    FILTER_FLAG_HOSTNAME
    )
    );'
    bool(false)

    View Slide

  11. 「空白」が含まれている
    11
    $ docker container run \
    --rm -it php:8.1.10-cli-alpine \
    php -r 'var_dump(
    filter_var(
    "www.example com",
    FILTER_VALIDATE_DOMAIN,
    FILTER_FLAG_HOSTNAME
    )
    );'
    bool(false)

    View Slide

  12. 「エクスクラメーション」が含まれ
    ている
    12
    $ docker container run \
    --rm -it php:8.1.10-cli-alpine \
    php -r 'var_dump(
    filter_var(
    "www.examp!e.com",
    FILTER_VALIDATE_DOMAIN,
    FILTER_FLAG_HOSTNAME
    )
    );'
    bool(false)

    View Slide

  13. 13

    View Slide

  14. 「ドメイン名」が含まれていない
    14
    $ docker container run \
    --rm -it php:8.1.10-cli-alpine \
    php -r 'var_dump(
    filter_var(
    "www",
    FILTER_VALIDATE_DOMAIN,
    FILTER_FLAG_HOSTNAME
    )
    );'
    string(3) "www"

    View Slide

  15. FILTER_FLAG_HOSTNAME
    の説明を思い出してみる
    15

    View Slide

  16. FILTER_FLAG_HOSTNAME
    の説明を思い出してみる
    16
    FILTER_FLAG_HOSTNAME を使うと、 ホス
    ト名 (アルファベットか数字で始まり、 アル
    ファベットか数字、もしくはハイフンのみが 含
    まれている必要があります) も追加で検証で
    きます。

    View Slide

  17. 17
    ホスト名ってどういうものだっけ?

    View Slide

  18. ホスト名ってこういうもの
    18
    ● localhost
    ● mail
    ● app
    ● www

    View Slide

  19. FILTER_FLAG_HOSTNAME
    を指定して実行
    19
    $ docker container run \
    --rm -it php:8.1.10-cli-alpine \
    php -r 'var_dump(
    filter_var(
    "localhost",
    FILTER_VALIDATE_DOMAIN,
    FILTER_FLAG_HOSTNAME
    )
    );'
    string(9) "localhost"

    View Slide

  20. 20
    そもそもドメイン名としてあるべき姿とは?

    View Slide

  21. ここにヒントがありそう
    21

    View Slide

  22. ここにヒントがありそう
    22
    ドメイン名が RFC 1034, RFC 1035,
    RFC 952, RFC 1123, RFC 2732, RFC
    2181, RFC 1123 に照らして正しいかを
    検証します。

    View Slide

  23. ドメイン名のバリデーションに関係しそう
    なRFCを読んでみた
    23
    ● RFC 952 (DOD INTERNET HOST TABLE SPECIFICATION)
    ● RFC 1034 (DOMAIN NAMES - CONCEPTS AND FACILITIES)
    ● RFC 1035 (DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION)
    ● RFC 1123 (Requirements for Internet Hosts -- Application and Support)
    ● RFC 2181 (Clarifications to the DNS Specification)

    View Slide

  24. RFC 952 (DOD INTERNET HOST
    TABLE SPECIFICATION)
    24
    ● ASSUMPTIONS
    ○ A "name" (Net, Host, Gateway, or Domain name) is a text string up to
    24 characters drawn from the alphabet (A-Z), digits (0-9), minus
    sign (-), and period (.). Note that periods are only allowed when they
    serve to delimit components of "domain style names". (See RFC-921,
    "Domain Name System Implementation Schedule", for background).
    ○ No blank or space characters are permitted as part of a name.
    ○ No distinction is made between upper and lower case.
    ○ The first character must be an alpha character.
    ○ The last character must not be a minus sign or period.

    View Slide

  25. 25
    ● 3.1. Name space specifications and terminology
    ○ The most common interpretation uses the root "." as either the
    single origin or as one of the members of the search list, so a
    multi-label relative name is often one where the trailing dot has
    been omitted to save typing.
    RFC 1034 (DOMAIN NAMES -
    CONCEPTS AND FACILITIES)

    View Slide

  26. 26
    ● 2.3.1. Preferred name syntax
    ○ The labels must follow the rules for ARPANET host names. They must start with a
    letter, end with a letter or digit, and have as interior characters only letters, digits, and
    hyphen. There are also some restrictions on the length. Labels must be 63 characters
    or less.
    ● 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
    ● 3.1. Name space definitions
    ○ Since every domain name ends with the null label of the root, a domain name is
    terminated by a length byte of zero.
    ○ 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.
    RFC 1035 (DOMAIN NAMES -
    IMPLEMENTATION AND
    SPECIFICATION)

    View Slide

  27. 27
    ● 2.1 Host Names and Numbers
    ○ The syntax of a legal Internet host name was specified in RFC-952
    [DNS:4]. One aspect of host name syntax is hereby changed: the
    restriction on the first character is relaxed to allow either a letter
    or a digit. Host software MUST support this more liberal syntax.
    ○ Host software MUST handle host names of up to 63 characters and
    SHOULD handle host names of up to 255 characters.
    RFC 1123 (Requirements for
    Internet Hosts -- Application and
    Support)

    View Slide

  28. 28
    ● 11. Name syntax
    ○ The length of any one label is limited to between 1 and 63 octets.
    ○ A full domain name is limited to 255 octets (including the
    separators).
    ○ The zero length full name is defined as representing the root of the
    DNS tree, and is typically written and displayed as ".".
    RFC 2181 (Clarifications to the
    DNS Specification)

    View Slide

  29. 29
    完全に理解した

    View Slide

  30. 30
    ● ドメイン名は253文字以下でなければならない(最大長の
    255オクテット - 予約分の2オクテット)
    ○ ドメイン名の表記は末尾のドットが省略されていることが
    多い(予約分の1オクテット)
    ○ すべてのドメイン名はrootのnullラベルで終了する(予約
    分の1オクテット)
    ドメイン名とは?

    View Slide

  31. 31
    ● ラベルに利用可能な文字
    ○ 英字(A-Z(大文字、小文字の区別はなし))
    ○ 数字(0-9)
    ○ マイナス記号(ハイフン)
    ドメイン名とは?

    View Slide

  32. 32
    ● ラベルは63文字以下でなければならない
    ○ ラベルの最初及び最後の文字は英数字でなければなら
    ない
    ○ ラベルの最初及び最後の文字としてマイナス記号は利用
    できない
    ○ ドットは区切り文字なのでラベルとしては利用できない
    ドメイン名とは?

    View Slide

  33. 文章ではわかりづらいので
    実際にDNSに登録してみた
    33

    View Slide

  34. ドメイン名が253文字以下は
    34
    $ echo -n
    'www.www.www.www.www.www.www.www.www.www.www.www.w
    ww.www.www.www.www.www.www.www.www.www.www.www.www
    .www.www.www.www.www.www.www.www.www.www.www.www.w
    ww.www.www.www.www.www.www.www.www.www.www.www.www
    .www.www.www.www.www.www.www.www.www.www.ww.tsunag
    i.me'|wc -c
    253

    View Slide

  35. 利用可能
    35
    $ dig
    www.www.www.www.www.www.www.www.www.www.www.www.ww
    w.www.www.www.www.www.www.www.www.www.www.www.www.
    www.www.www.www.www.www.www.www.www.www.www.www.ww
    w.www.www.www.www.www.www.www.www.www.www.www.www.
    www.www.www.www.www.www.www.www.www.www.ww.tsunagi
    .me +short
    75.2.60.5

    View Slide

  36. ドメイン名が254文字以上は
    36
    $ echo -n
    'www.www.www.www.www.www.www.www.www.www.www.www.w
    ww.www.www.www.www.www.www.www.www.www.www.www.www
    .www.www.www.www.www.www.www.www.www.www.www.www.w
    ww.www.www.www.www.www.www.www.www.www.www.www.www
    .www.www.www.www.www.www.www.www.www.www.www.tsuna
    gi.me'|wc -c
    254

    View Slide

  37. 利用不可
    37
    $ dig
    www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.w
    ww.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.ww
    w.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www
    .www.www.www.www.www.www.www.www.www.tsunagi.me +shortdig:
    'www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.
    www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.w
    ww.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.www.ww
    w.www.www.www.www.www.www.www.www.www.tsunagi.me' is not a legal
    IDNA2008 name (domain name longer than 255 characters), use +noidnin

    View Slide

  38. ラベルが63文字以下は
    38
    $ echo -n
    'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
    wwwwwwwwwwwwww' |wc -c
    63

    View Slide

  39. 利用可能
    39
    $ dig
    wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
    wwwwwwwwwwwwww.tsunagi.me +short
    75.2.60.5

    View Slide

  40. ラベルが64文字以上は
    40
    $ echo -n
    'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
    wwwwwwwwwwwwwww' | wc -c
    64

    View Slide

  41. 利用不可
    41
    $ dig
    wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
    wwwwwwwwwwwwww.tsunagi.me +short
    dig:
    'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
    wwwwwwwwwwwwwww.tsunagi.me' is not a legal
    IDNA2008 name (domain label longer than 63
    characters), use +noidnin

    View Slide

  42. 英字、数字、マイナス記号が
    ラベルの文字列として利用可能
    42
    $ dig abc-123.tsunagi.me +short
    75.2.60.5
    $ dig
    abcdefghijklmnopqrstuvwxyz-1234567890.tsunagi.me
    +short
    75.2.60.5

    View Slide

  43. 43
    ドメイン名がどういうものかは理解した

    View Slide

  44. 44
    ● ドメイン名は253文字以下でなければならない(最大長の 255オクテット - 予約分の2オクテット)
    ○ ドメイン名の表記は末尾のドットが省略されていることが多い(予約分の 1オクテット)
    ○ すべてのドメイン名は rootのnullラベルで終了する(予約分の 1オクテット)
    ● ラベルに利用可能な文字
    ○ 英字(A-Z(大文字、小文字の区別はなし))
    ○ 数字(0-9)
    ○ マイナス記号(ハイフン)
    ● ラベルは63文字以下でなければならない
    ○ ラベルの最初及び最後の文字は英数字でなければならない
    ○ ラベルの最初及び最後の文字としてマイナス記号は利用できない
    ○ ドットは区切り文字なのでラベルとしては利用できない
    しかし、このバリデーション
    の実装は面倒

    View Slide

  45. では、filter_var の
    FILTER_VALIDATE_DOMAIN は具体的に
    何をチェックしているんだろう?
    45

    View Slide

  46. /php-8.1.10/ext/filter/logical_filters.c
    を読んでみた
    46

    View Slide

  47. 47
    507 static int _php_filter_validate_domain(char * domain,
    size_t len, zend_long flags) /* {{{ */
    508 {


    525 /* The total length cannot exceed 253 characters
    (final dot not included) */
    526 if (l > 253) {
    527 return 0;
    528 }
    ドメイン名は253文字以下
    254文字以上はfalse

    View Slide

  48. 48
    507 static int _php_filter_validate_domain(char * domain,
    size_t len, zend_long flags) /* {{{ */
    508 {


    519 /* Ignore trailing dot */
    520 if (l > 0 && *t == '.') {
    521 e = t;
    522 l--;
    523 }
    末尾のドットを省略
    末尾のドットは無視する
    (RFCの仕様通り)

    View Slide

  49. 49
    507 static int _php_filter_validate_domain(char * domain,
    size_t len, zend_long flags) /* {{{ */
    508 {



    530 /* First char must be alphanumeric */
    531 if(*s == '.' || (hostname &&
    !isalnum((int)*(unsigned char *)s))) {
    532 return 0;
    533 }
    ラベルの最初及び最後の文字
    は英数字
    最初の文字が「.」(ドット)または英数
    字以外の場合はfalse

    View Slide

  50. 535 while (s < e) {
    536 if (*s == '.') {
    537 /* The first and the last
    character of a label must be alphanumeric */
    538 if (*(s + 1) == '.' || (hostname
    && (!isalnum((int)*(unsigned char *)(s - 1)) ||
    !isalnum((int)*(unsigned char *)(s + 1))))) {
    539 return 0;
    540 }


    50
    ラベルの最初及び最後の文字
    は英数字
    最初及び最後の文字が「.」(ドット)または
    英数字以外の場合はfalse

    View Slide

  51. 51
    535 while (s < e) {
                   ・
                   ・
    544 } else {
    545 if (i > 63 || (hostname && *s !=
    '-' && !isalnum((int)*(unsigned char *)s))) {
    546 return 0;
    547 }
                   ・
                   ・
    ラベルは英数字、ハイフン
    63文字以下
    ラベルが64文字以上、または、
    英数字、ハイフン以外はfalse

    View Slide

  52. 52
    ● ドメイン名は253文字以下でなければならない(最大長の 255オクテット - 予約分の2オクテット)
    ○ ドメイン名の表記は末尾のドットが省略されていることが多い(予約分の 1オクテット)
    ○ すべてのドメイン名は rootのnullラベルで終了する(予約分の 1オクテット)
    ● ラベルに利用可能な文字
    ○ 英字(A-Z(大文字、小文字の区別はなし))
    ○ 数字(0-9)
    ○ マイナス記号(ハイフン)
    ● ラベルは63文字以下でなければならない
    ○ ラベルの最初及び最後の文字は英数字でなければならない
    ○ ラベルの最初及び最後の文字としてマイナス記号は利用できない
    ○ ドットは区切り文字なのでラベルとしては利用できない
    あれ?全部網羅されている

    View Slide

  53. filter_var、疑ってゴメンなさい
    53

    View Slide

  54. 今回必要なバリデーションの仕様としては
    ・ホスト名のみ(ドメイン名を含まない)はNG
    ・ネイキッドドメイン(Zone Apex)のみはOK
    この条件を満たせばよいことがわかったので
    54

    View Slide

  55. 55
    バリデーションの例
    function validate(string $domain): bool
    {
    return (
    (count(explode('.', $domain)) >= 2) &&
    filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)
    );
    }
    var_dump(validate('www')); // false
    var_dump(validate('www.example.com')); // true
    var_dump(validate('www.example,com')); // false
    var_dump(validate('www.example com')); // false
    var_dump(validate('www.examp!e.com')); // false
    「.」(ドット)で区切られた数が
    2以上の場合という条件を追加

    View Slide

  56. ここまで話した内容は
    56

    View Slide

  57. ひさてるさん(@tanakahisateru)が
    7年前に詳しく解説されています
    57
    ● http:/
    /blog.a-way-out.net/blog/2015/02/16/validate-hostname/

    View Slide

  58. DNSに登録されている
    ドメイン名であれば
    58
    おまけ1

    View Slide

  59. checkdnsrr
    59
    $ docker container run \
    --rm -it php:8.1.10-cli-alpine \
    php -r 'var_dump(
    checkdnsrr("twitter.com", "A")
    );'
    bool(true)

    View Slide

  60. filter_var / FILTER_VALIDATE_IP
    gethostbyname
    60
    $ docker container run \
    --rm -it php:8.1.10-cli-alpine \
    php -r 'var_dump(
    filter_var(
    gethostbyname("twitter.com"),
    FILTER_VALIDATE_IP
    )
    );'
    string(12) "104.244.42.1"

    View Slide

  61. といったチェック方法もあります
    61

    View Slide

  62. Qiitaにまとめました
    62
    おまけ2

    View Slide

  63. 【 tsunagi.me 】
    というドメインでPodcastを
    やってるので聞いてください
    63
    最後に宣伝

    View Slide

  64. 今回話さなかったことについて
    64

    View Slide

  65. 65
    皆が本当に
    聞きたいのはこれでしょ?
    PHPカンファレンス福岡2023の
    開催可否については委員長まで
    @seike460

    View Slide

  66. Thanks!
    Have a good programming!!
    66

    View Slide