This post is Private. Only a writer or those who know its URL can access this post.

Improve article
Show article in Markdown
Report article
Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

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

参考文献

expajp
マネジメントに関心のあるエンジニアです。仕事ではRails、趣味ではRubyの他にVueやGASも書いています。
http://expajp-tech.hatenablog.com/
mohikanz
エンジニアのための雑談コミュニティ
https://mohikanz.slack.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away