JavaScriptでRFC違反ではないメールアドレスの構造とバリデーション実装方法

Mail_RFC_Validate

こんにちはフロントエンドエンジニアのまさにょんです!

今回は、JavaScriptでRFC違反ではないメールアドレスの構造とバリデーション実装方法について解説していきます。

JavaScriptでRFC違反ではないメールアドレスの構造とバリデーション実装方法

メールアドレスの構造を理解する

メールアドレスは、@より前の「ローカル部分」(local part)と、@より後の「ドメイン部分」(domain part)の2つのパーツに分けられます。

つまり「 ローカル部分@ドメイン部分 」という構造でできています。

また、ローカル部分は、ユーザー名やアカウント名などとも呼ばれ、メールアドレスを一意に識別するための重要な役割を果たします。

次のメールアドレスは、robotamaがローカル部分、gmail.comがドメイン部分です。

ローカル部分@ドメイン部分
robotama@gmail.com

RFC違反メールアドレス とは?

RFC違反メールアドレスとは、電子メールアドレスがインターネットの標準的な仕様であるRFC(Request for Comments)に準拠していないことを指します。

RFCは、インターネットのプロトコルや規格などを定める文書であり、

RFCに準拠していない電子メールアドレスは、インターネット上で正しく処理されない可能性があります。

例えば、RFC違反のメールアドレスとしては、ドメイン部分が無効であったり、ローカル部分に不正な文字が含まれていたりする場合があります。

RFC違反メールアドレスを使用すると、メールが正しく配信されなかったり、迷惑メールとして処理されたりする可能性があります。

なので、正しい形式のメールアドレスを使用することが重要です。

RFC準拠でないとメールアドレスの登録ができないWebサイトなどもあります。

使用可能なメールアドレスについて(RFC準拠)

RFCに基づくメールアドレスの仕様

RFCに基づくメールアドレスの仕様をまとめると、次のようになります。

最大文字数

ローカル部分、ドメイン部分、「ローカル部分@ドメイン部分」の全体で、それぞれ最大文字数が決まっています。

  1. ローカル部分は、最大64文字。
  2. ドメイン部分は、最大253文字。
  3. ローカル部分@ドメイン部分の全体では、最大254文字。 

ローカル部分に使える文字の種類

メールアドレスで、全角文字は使えません。すべて半角になります。

ローカル部分に使える文字の種類の一覧は、

RFC 5322 & 5321に沿ったメールアドレス(local-part)に使える文字まとめ から引用します。

  1. 無条件で使える: 81文字
    1. アルファベット(大文字 & 小文字)52文字 → A~Z, a~z
    2. 数字10文字 → 0~9
    3. 記号19文字 → ! # $ % & ' * + - / = ? ^ _ ` { | } ~
  2. 先頭と末尾以外、かつ連続しなければ使える(末尾=@の直前): 1文字
    1. ドット → .
  3. ダブルクオートでくくると使える: 93文字
    1. 無条件で使えるもの81文字
    2. ドット(連続しよう可) → .
    3. 記号10文字 → ( ) , : ; < > @ [ ]
  4. ダブルクオートでくくる & バックスラッシュをつけると使える: 97文字
    1. ダブルクオートでくくると使えるもの93文字
    2. バックスラッシュと連続すると使える4文字(最後の2つは半角スペースとタブです) → " \ タブ
引用元: RFC 5322 & 5321に沿ったメールアドレス(local-part)に使える文字まとめ

RFC違反メールアドレスのバリデーション実装の要件定義

今回のRFC違反ではないメールアドレスのバリデーション実装の要件定義は、次のとおりです。

次のようなNGパターンのメールアドレスは、RFC違反としてバリデーションでErrorとします。

  1. @の前のローカル部分で、64文字以上になっている。
  2. ドメイン名が255文字以上の長さになっている。
  3. メールアドレス全体で256文字以上になっている。
  4. .(ドット)が、ローカル部分で、2つ以上連続している。
  5. .(ドット)を、ローカル部分の最初と最後(@の直前)に使っている。
  6. -(ハイフン)が、ローカル部分の先頭または末尾にある。
  7. -(ハイフン)が、ドメイン名の一番最後にある。
  8. ドメイン名が数字だけで構成されている。
  9. ユーザー名またはドメイン名に、RFCに定義されていない特殊文字が含まれている。
    • ( ) , : ; < > @ [ ]

RFC違反メールアドレスのバリデーション実装のSampleCode

RFC違反メールアドレスのバリデーション実装のSampleCodeは、次のとおりです。

function validateEmail(email) {
  // メールアドレス全体で256文字以上になっている
  if (256 <= email.length) {
    return false;
  }

  // ローカル部分とドメイン名に分割
  const [local, domain] = email.split("@");

  // @が含まれていない場合は無効なメールアドレス
  if (!domain) {
    return false;
  }

  // ローカル部分で、64文字以上になっている
  if (64 <= local.length) {
    return false;
  }

  // ドメイン名が255文字以上の長さになっている
  if (255 <= domain.length) {
    return false;
  }

  // .(ドット)が、ローカル部分で、2つ以上連続している
  if (local.match(/\.{2,}/)) {
    return false;
  }

  // .(ドット)を、ローカル部分の最初と最後(@の直前)に使っている
  if (local.startsWith(".") || local.endsWith(".")) {
    return false;
  }

  // -(ハイフン)が、ローカル部分 or ドメイン部分の先頭または末尾にある
  if (
    local.startsWith("-") ||
    local.endsWith("-") ||
    domain.startsWith("-") ||
    domain.endsWith("-")
  ) {
    return false;
  }

  // ドメイン名が数字だけで構成されている
  if (domain.match(/^\d+$/)) {
    return false;
  }

  // ユーザー名またはドメイン名に、RFCに定義されていない特殊文字(( ) , : ; < > @ [ ])が含まれている
  if (local.match(/[ (),:;<>@[\]]/) || domain.match(/[ (),:;<>@[\]]/)) {
    return false;
  }

  // すべての条件を満たした場合は有効なメールアドレス
  return true;
}

// [ 正常系ブロック ]
console.log(validateEmail("robotama@gmail.com")); // true
console.log(validateEmail("robotama@example.co.jp")); // true
console.log(validateEmail("robotama@gmail.com")); // true
console.log(validateEmail("robotama-name@gmail.com")); // true
console.log(validateEmail("robotama.name@gmail.com")); // true
console.log("-------------------------------------------------------");

// [ Validate_Errorブロック ]
// ローカル部分: 64文字
let localLongMail =
  "robotamarobotamarobotamarobotamarobotamarobotamarobotamarobotama@gmail.com";
console.log(validateEmail(localLongMail)); // false
console.log(validateEmail("robotama")); // false
console.log(validateEmail("..robotama@gmail.com")); // false
console.log(validateEmail("robotama..puru@gmail.com")); // false
console.log(validateEmail(".robotama@gmail.com")); // false
console.log(validateEmail("robotama.@gmail.com")); // false
console.log(validateEmail("robotama@gmail.com-")); // false
console.log(validateEmail("-robotama@gmail.com")); // false
console.log(validateEmail("robotama-@gmail.com")); // false
console.log(validateEmail("robotama@12345")); // false
console.log(validateEmail("robo[]tama@gmail.com")); // false
console.log(validateEmail("robo<tama@gmail.com")); // false
console.log(validateEmail("robo,tama@gmail.com")); // false

RFC違反メールアドレスのバリデーション実装のSample (Error_MSGバージョン)

次のSampleCodeは、RFC違反メールアドレスのバリデーション実装のError_Messageを表示するためのバージョンです。

// RFC違反_メールアドレス・バリデーション => false: 成功, Error_MSG: 失敗
export function validatRfcEmail(email: string) {
    // メールアドレス全体で256文字以上になっている
    if (256 <= email.length) {
        return 'メールアドレス全体で256文字以上になっています';
    }

    // ローカル部分とドメイン名に分割
    const [local, domain] = email.split('@');

    // @が含まれていない場合は無効なメールアドレス
    if (!domain) {
        return '@が含まれていない場合は無効なメールアドレスです';
    }

    // ローカル部分で、64文字以上になっている
    if (64 <= local.length) {
        return '@より前の部分が、64文字以上になっています';
    }

    // ドメイン名が255文字以上の長さになっている
    if (255 <= domain.length) {
        return '@より後の部分が255文字以上の長さになっています';
    }

    // .(ドット)が、ローカル部分で、2つ以上連続している
    if (local.match(/\.{2,}/)) {
        return '.(ドット)が、@より前の部分で、2つ以上連続しています';
    }

    // .(ドット)を、ローカル部分の最初と最後(@の直前)に使っている
    if (local.startsWith('.') || local.endsWith('.')) {
        return '.(ドット)を、ローカル部分の最初と最後(@の直前)に使っています';
    }

    // -(ハイフン)が、ローカル部分 or ドメイン部分の先頭または末尾にある
    if (
        local.startsWith('-') ||
        local.endsWith('-') ||
        domain.startsWith('-') ||
        domain.endsWith('-')
    ) {
        return '-(ハイフン)が先頭・末尾または、@の前後にあります';
    }

    // ドメイン名が数字だけで構成されている
    if (domain.match(/^\d+$/)) {
        return '@より後の部分が、数字だけで構成されています';
    }

    // ユーザー名またはドメイン名に、RFCに定義されていない特殊文字(( ) , : ; < > @ [ ])が含まれている
    if (local.match(/[ (),:;<>@[\]]/) || domain.match(/[ (),:;<>@[\]]/)) {
        return 'RFCに定義されていない特殊文字(( ) , : ; < > @ [ ])が含まれています';
    }
    // すべての条件を満たした場合は有効なメールアドレス
    return false;
}

JavaScript書籍 Ver. 中級-上級者向け

JavaScript書籍 Ver. 初級者向け

Twitterやってます!Follow Me!

神聖グンマー帝国の逆襲🔥

神聖グンマー帝国の科学は、世界一ぃぃぃぃぃぃ!!!!!

参考・引用

  1. RFC 5322 & 5321に沿ったメールアドレス(local-part)に使える文字まとめ
  2. 使用可能なメールアドレスについて(RFC準拠)
  3. 特殊な形式のアドレス(RFC違反アドレス)のご利用について

最近の投稿