LoginSignup

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 5 years have passed since last update.

その正規表現、異議あり!〜 正しいメールアドレスを判別しよう

Last updated at Posted at 2019-03-13
1 / 30

自己紹介

  • Shu OGAWARA(@expajp )
    • リンカーズ株式会社
    • 社会人3年目
    • Rails歴は1年半くらい
    • 出身は鳥取で大学は神戸
    • 趣味は合唱

正規表現、みなさんご存知ですよね


正規表現(せいきひょうげん、英: regular expression)とは、文字列の集合を一つの文字列で表現する方法の一つである。正則表現(せいそくひょうげん)とも呼ばれ、形式言語理論の分野では比較的こちらの訳語の方が使われる。

引用:正規表現 - https://ja.wikipedia.org/wiki/正規表現


要は、文字列が特定の条件を満たすかどうか調べるための表現


どうやって使うか? :thinking:


Railsアプリでよくある使われ方

ユーザ登録時に、メールアドレスが正しいかどうか調べる


こんな感じで正規表現を組みたい


できた

regexp = /^([a-z0-9_+-]\.)*[a-z0-9_]+@[a-z0-9\-]+(\.[a-z0-9\-]+)*\.[a-z]+$/

可視化するとこんな感じ

regexp01.png


なんとなくよさそう


igiari.png


この正規表現には問題がある


脆弱性がある


正規表現に脆弱性? :thinking:


正規表現の脆弱性

  • ポイントは、正規表現の処理が非常に重いということ
    • 1字1字、巨大な文字集合に合致するか調べる
    • 一致するかどうかは最後まで調べてみないと分からない
    • 特に、「一致しない」場合はすべてのパターンを試さないと分からない
  • 正規表現では、(本来は)文字列の長さに制限を設けられない

どういうこと?

naruto.jpg


長くて正規表現に一致しない文字列をメールアドレス欄に放り込むだけでDoS攻撃ができる


DoS攻撃

DoS攻撃(ドスこうげき)(英:Denial of Service attack)は、情報セキュリティにおける可用性を侵害する攻撃手法のひとつ。

ウェブサービスを稼働しているサーバやネットワークなどのリソース(資源)に意図的に過剰な負荷をかけたり脆弱性をついたりする事でサービスを妨害する。

DoS攻撃 - https://ja.wikipedia.org/wiki/DoS攻撃

特に、正規表現を使ったDoSはReDoSと呼ばれる


実際にやってみよう

# 引用:正規表現でのメールアドレスチェックは見直すべき – ReDoS – yohgaki's blog 
# https://blog.ohgaki.net/redos-must-review-mail-address-validation

# 文字列の長さ:処理時間 で順に出力する
n = 5;
while n < 12 do
  s = "username@host"+".abcde"*n+"."
  start = Time.now();
  p /^([a-z0-9_+-]\.)*[a-z0-9_]+@[a-z0-9\-]+(\.[a-z0-9\-]+)*\.[a-z]+$/i =~ s
  p s.length.to_s + ': ' + (Time.now() - start).to_s;
  n += 1
end


さらに悲しいお知らせ


メールアドレスの定義

RFC5321(Simple Mail Transfer Protocol)(日本語訳)によると、

4.5.3.1.1. Local-part
ユーザー名または他の local-part の最大長は 64 オクテットである。

4.5.3.1.2. ドメイン
ドメインの名前または数値の最大長は 255 オクテットである。

4.5.3.1.3. パス
reverse-path または forward-path の最大長は 256 オクテットである(句読点や要素区切りを含む)。

また、RFC1035(DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION)(日本語訳)によると、ドメイン名は

2.3.4. サイズ制限
DNS の様々なオブジェクトやパラメータはサイズ制限を持つ。それらを以下に示す。簡単に変更できるものもあれば、根本的なものもある。

ラベル 63 オクテット以下

名前 255 オクテット以下


メールアドレスの定義

つまり、

  • メールアドレス全体は256文字以内
  • ユーザ名(@の前)は64文字以内
  • ドメインは255文字以内
  • ドメインのラベルは63文字以内
    • test.example.com <- 例えばここ

長さは正規のメールアドレスの範囲内


どうすればよいのか?


文字列を区切って短くし、
シンプルな正規表現で判定する


ユーザ名の場合

  • 使える記号は. - + _
  • 64文字以内
  • 記号以外の部分は英数字

ユーザ名の場合

email = 'hoge_hoge.piyo-piyo+fuga@example.com'

# ユーザ名を切り出し
username = email.split('@').first

# 64文字以上はアウト
return false if username.length > 64

# 記号で区切り、それぞれ英数字かどうか調べる
validities = username.split(/[\.\+\-\_]/).reject(&:empty?).map{ |s| /^[a-z0-9A-Z]+$/ === s }

# すべてtrueでないならfalseを返す
return false unless validities.inject(&:&)

# 今度は文字で区切り、それぞれ1文字の記号かどうか調べる
validities = username.split(/[a-z0-9A-Z]+/).reject(&:empty?).map{ |s| /^[\.\+\-\_]$/ === s }

# すべてtrueでないならfalseを返す
return false unless validities.inject(&:&)


まとめ


まとめ

  • 正規表現でDoS攻撃をかける攻撃手法がある
  • メアドなど、複雑な正規表現は避けるべき
  • 仕様をちゃんと読んで、短い単位でチェックをすることを心がけよう

参考文献

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up