Slide 1

Slide 1 text

メール認証とRuby 2024/03/06 Gotanda.rb#57 ikaruga / Atsushi Katsuura 1

Slide 2

Slide 2 text

誰 ikaruga / Atsushi Katsuura GMOペパボ株式会社 EC事業部 CREチーム puts Resolv::DNS.new.getresources( 'me.ikaruga.org', Resolv::DNS::Resource::IN::TXT ).map(&:data) => ["GITHUB=ikaruga777", "TWITTER=@UVB_76", "WORK=GMO Pepabo Inc.", "[email protected]"] 2

Slide 3

Slide 3 text

https://support.google.com/a/answer/81126?hl=ja 3

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

メール認証のおさらい 色々要件はあったけど、基本SPF,DKIM,DMARC 全てはメールのなりすましを防ぐための仕組み 5

Slide 6

Slide 6 text

メールヘッダー 二種類のFrom ヘッダーFrom メールヘッダーの From エンベロープFrom メールヘッダーの Return-Path 6

Slide 7

Slide 7 text

(ちょうどいま来たメールを例に) Gmailだったらメールのメニューから「メッセージのソースを表示」をポチるとヘッダーを確 認できる # ヘッダーFrom From: Duolingo # エンベロープFrom Return-Path: <0100018e1341b06c-6b1b2b73-30d1-49fc-9788-8b3c5133d0c5-000000@bounces.duolingo.com> 7

Slide 8

Slide 8 text

SPF Sender Policy Framework メール送信元のドメインが許可されているかどうかをチェックする エンベロープFromのドメインでTXTレコード引くと v=spf1 から始まる値が取れる。 ip4: や ip6: の後ろにIPアドレスが含まれているので、そこで送信元のIPアドレ スが引けたらPASS $ dig spf-external.shop-pro.jp TXT +short "v=spf1 ip4:210.175.7.1/32 include:srs0260.cuenote.jp ~all 8

Slide 9

Slide 9 text

SPF IPアドレス以外にも a , mx , include , redirect の mechanism ではドメインを指定 できる。 ドメインに当たったらそのドメインで再帰的にTXTレコードを引いていく。 ルックアップの回数は10回まで。 https://tex2e.github.io/rfc-translater/html/rfc7208.html 4.6.4. DNS Lookup Limits にその話がある 9

Slide 10

Slide 10 text

(Googleから来てたメールを例に取る) メールヘッダー Return-Path: <3H5XiZRAKCgozk8wox32-xy1ozv8qyyqvo.mywsuk14qkHHHqwksv.myw@scoutcamp.bounces.google.com> Received: from mail-sor-f69.google.com (mail-sor-f69.google.com. [209.85.220.69]) From: Google Payments 209.85.220.69 が送信元のIPアドレス 10

Slide 11

Slide 11 text

Return-PathのドメインでSPFを引くと $ dig 3h5xizrakcgozk8wox32- xy1ozv8qyyqvo.mywsuk14qkhhhqwksv.myw@scoutcamp.bounces.google.com TXT +short "v=spf1 redirect=_spf.google.com" $ dig _spf.google.com TXT +short $ dig _spf.google.com TXT +short "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all" $ dig _netblocks.google.com TXT +short "v=spf1 ip4:35.190.247.0/24 ..( 中略).. ip4:209.85.128.0/17 .... ~all" 11

Slide 12

Slide 12 text

(Googleから来てたメールを例に取る) irb(main):003> IPAddr.new('209.85.128.0/17').include?('209.85.220.69') => true 12

Slide 13

Slide 13 text

DKIM DomainKeys Identified Mail メールを送信する際にメールの内容を用いて署名を作って認証する仕組み 送信されるメールサーバに秘密鍵を保存する 生成された署名は DKIM-Signature ヘッダーに付与される 受信者はメールヘッダーの DKIM-Signature ヘッダーの d, s タグからレコードを見つ ける dはドメイン、sはセレクタ(鍵ローテのために複数認証系を用意してる) "#{s}._domainkey.#{d} でTXTレコードを引くと公開鍵が引けるので、それを使 って認証する。 13

Slide 14

Slide 14 text

(Googleから来てたメールを例に取る) メールヘッダー DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1709348127; x=1709952927; dara=google.com; h=to:from:subject:message-id:feedback-id:reply-to:date:mime-version :from:to:cc:subject:date:message-id:reply-to; "#{s}._domainkey.#{d}" でTXTレコードを引く p= のあとが公開鍵 $ dig 20230601._domainkey.google.com TXT +short "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AM...( 略)" 14

Slide 15

Slide 15 text

DMARC Domain-based Message Authentication, Reporting and Conformance SPFとDKIMに加えて追加の認証をすることでなりすましを防ぐ SPFアライメント、DKIMアライメントを追加で認証する さらに、認証が通らなかったときのメールの監視の仕組みも定義されていて、送信元の サーバー管理者が通告されたSPAMと判定されたメールの内容について把握することが できる。 設定もDNSレコードで行う 15

Slide 16

Slide 16 text

DMARCレコードの実例 $ dig _dmarc.google.com TXT +short "v=DMARC1; p=reject; rua=mailto:[email protected]" $ dig _dmarc.shop-pro.jp TXT +short "v=DMARC1; p=none; pct=100; adkim=r; aspf=r; rua=mailto:[email protected]; ruf=mailto:[email protected];" 16

Slide 17

Slide 17 text

アライメント SPFアライメント エンベロープFromとヘッダーFromのドメインが一致しているかどうか DKIMアライメント ヘッダーFromのドメインと DKIM-Signature ヘッダーの d タグのドメインが一致している かどうか 17

Slide 18

Slide 18 text

ref: https://speakerdeck.com/azumakuniyuki/gu-iji-shu-nituite-smtpxian-dai-shi-qing- tumamishi-i?slide=14 YAPC::HiroshimaでされていたSMTPのセッションが簡潔にまとまっていて最高。 古い技術について—SMTP現代事情つまみ食い— - Speaker Deck 18

Slide 19

Slide 19 text

ブログ SendGrid, ベアメールのブログにわかりやすい記事がたくさんあって便利。 https://sendgrid.kke.co.jp/blog/ https://baremail.jp/blog/ 19

Slide 20

Slide 20 text

一応Rubyの話 20

Slide 21

Slide 21 text

設定されていることをどうやって確認するか SPF, DKIM, DMARCいずれもDNSレコードの内容を確認していく必要がある。 21

Slide 22

Slide 22 text

Resolv 標準ライブラリにDNS引いてくれるくんがいて便利。 https://github.com/ruby/resolv https://docs.ruby-lang.org/ja/3.3/library/resolv.html 22

Slide 23

Slide 23 text

誰 ikaruga / Atsushi Katsuura GMOペパボ株式会社 EC事業部 CREチーム puts Resolv::DNS.new.getresources( # ← これ 'me.ikaruga.org', # ← これ Resolv::DNS::Resource::IN::TXT # ← これ ).map(&:data) # ← これ => ["GITHUB=ikaruga777", "TWITTER=@UVB_76", "WORK=GMO Pepabo Inc.", "[email protected]"] 23

Slide 24

Slide 24 text

パーサーがある https://github.com/trailofbits/spf-query SPFレコードのパーサーもいて便利 https://github.com/trailofbits/dkim-query DKIMレコードのパーサーもいて便利 https://github.com/trailofbits/dmarc DMARCレコードのパーサーもいて便利 24

Slide 25

Slide 25 text

spf-query 使ってみる require 'bundler/inline' gemfile do source 'https://rubygems.org' gem 'spf-query' end domain = ARGV.first q = SPF::Query::Record.query(domain) def query(q, lookup_count) q.mechanisms.each do |mechanism| case mechanism.name when :ip4, :ip6 puts mechanism.value when :include, :redirect lookup_count += 1 return if lookup_count > 10 puts ">>" + mechanism.value # _spf サブドメインを暗黙的に引いてしまうらしい # https://github.com/trailofbits/spf-query/issues/10 records = Resolv::DNS.new.getresources(mechanism.value, Resolv::DNS::Resource::IN::TXT) spf_record = records.find do |record| record.strings.join.include?('v=spf1') end spf_record_value = spf_record.strings.join q = SPF::Query::Parser.parse(spf_record_value) query(q, lookup_count) when :a lookup_count += 1 # Resolv::DNS::Resource::IN::A でレコードを引く when :mx lookup_count += 1 # Resolv::DNS::Resource::IN::MX でレコードを引く when :ptr, :exsists lookup_count += 1 # 頑張って実装 end end end query(q, 0) 25

Slide 26

Slide 26 text

$ ruby parse_spf.rb _spf.google.com >>_netblocks.google.com 35.190.247.0/24 64.233.160.0/19 66.102.0.0/20 66.249.80.0/20 72.14.192.0/18 74.125.0.0/16 108.177.8.0/21 173.194.0.0/16 209.85.128.0/17 216.58.192.0/19 216.239.32.0/19 >>_netblocks2.google.com 2001:4860:4000::/36 2404:6800:4000::/36 2607:f8b0:4000::/36 2800:3f0:4000::/36 2a00:1450:4000::/36 2c0f:fb50:4000::/36 >>_netblocks3.google.com 172.217.0.0/19 172.217.32.0/20 172.217.128.0/19 172.217.160.0/20 172.217.192.0/19 172.253.56.0/21 172.253.112.0/20 108.177.96.0/19 35.191.0.0/16 130.211.0.0/22 26

Slide 27

Slide 27 text

まとめ 最近話題になっていたDNSを使ったメール認証のしくみのおさらいをしました Rubyでドメインに対して適切なレコードが設定されているかを確認するを1つ紹介しま した 懇親会で喋れたら喋りたい ワンクリック購読解除の適用範囲 27

Slide 28

Slide 28 text

仕事の話(宣伝) 3/15~の Ya8 2024 - ヤパチー 令和六年最新版(仮) で同僚が現場の話をする予定です(採択さ れていました) 以下のような現場の話が聞ける可能性があります 「メールサーバー」を自前で運用している 自前のメールサーバーやSaasを通して、ユーザーが設定した「独自ドメイン」でメール を送るための機能を提供している https://github.com/hachiojipm/ya8-2024-cfp/issues/32 28