ITの隊長のブログ

ITの隊長のブログです。Pythonを使って仕事しています。最近は機械学習をさわりはじめたお(^ω^ = ^ω^)

【Play Framework2.x】Mail Pluginを使って、なりすましっぽいメールを送信する(できました)

スポンサードリンク

「なりすまし? ダメにきまってんでしょ!?」

そう却下できたらいいんですが、世の中色々な人や仕事がありますので、そうもいかなかった。

業務改善のためにいわゆる”なりすまし”のメールを、業者向けに送らないといけなかった。

不特定多数に送信するのはNGだが、自分たちのサーバやソリューション向けに送信するのは100歩ゆずっておkすることにした。

Play Frameworkのmail pluginを使っていました。以前このブログで記事書いたことがある。

www.aipacommander.com

MailerAPI mail = init();
mail.setFrom("名字 名前 様 <from@gmail.com>");
mail.setRecipient("to@gmail.com");
mail.setSubject("テストの件名ですよー");
mail.send(views.html.mail.contents.render().toString().trim());

こんな感じ。

しかし。。。

使っているメールサーバが外部のサービスのサーバだっため、登録されているメールアドレスのドメインアドレス以外はエラーするって仕様だったのだ! 隊長ピンチ!

じゃあどうしよ! しかしわたしは元(エセ)ネットワークエンジニアだったのでメールの仕様はこちらのサイトで勉強していたのね。

3 Minutes Networking

メーラーで確認できるtoとfromは基本書き換え可能なため、「headerいじればええんじゃね?」ってひらめいたのだ。

じゃあ実装変えます。

MailerAPI mail = init();
mail.setFrom("from@gmail.com");
mail.addHeader("from", "名字 名前 様 <from@gmail.com>"); // これを追加
mail.setRecipient("to@gmail.com");
mail.setSubject("テストの件名ですよー");
mail.send(views.html.mail.contents.render().toString().trim());

github.com

addHeaderっていうメソッドがあったので、そこでheaderを追加するようにした。

おkおk〜。っと思いきや。

あれあれあれあれ〜? めっちゃエラーがでてる。。。

iandeth.dyndns.org

ぐぐったらこちらの記事が。。。なるほど。マルチバイト悪だな。

じゃあこのコードをJavaで実装するようにしました。

MailerAPI mail = init();
mail.setFrom("from@gmail.com");
mail.addHeader("from", encodeFromString("名字 名前 様", "from@gmail.com>"));
mail.setRecipient("to@gmail.com");
mail.setSubject("テストの件名ですよー");
mail.send(views.html.mail.contents.render().toString().trim());

// ... 省略
private static String encodeFromString(String fromString, String mailAddress) {
    String fromText = "";
    try {
        String encodeFromText = new String(fromString.getBytes("ISO2022JP"));
        String baseEncodeFromText = new String(Base64.encodeBase64(encodeFromText.getBytes()));
        fromText = "=?iso-2022-jp?B?" + baseEncodeFromText + "?=" + " <" + mailAddress + ">";
    } catch (Exception e) {
        Logger.trace(e.toString());
    }
    return fromText;
}

Base64は↓のライブラリをimportした

weblabo.oscasierra.net

やっていることは単純で

  1. ISO2022JPでエンコードして
  2. ↑をBase64エンコードして
  3. エンコードした文字列の先頭に"=?iso-2022-jp?B?"と、末尾に"?="をつける
  4. ↑の文字列に"<メールアドレス>"の文字列を半角空白文字で結合する

これでよし。これでエラーが発生せず、安全(?)でなりすましできるようになりました。

と思いきや。。。。この↑のコードではうまくいかないことがわかりました\(^o^)/オワタ

どうやら、"duplicate from"にひっかかったみたいで、サーバに渡せてもメールサーバがエラーを送信者に返す事態に。

うぐぐぐ・・・!どないせーちゅーねん。

ということで、色々探した結果 Mail Pluginではできないことが発覚しました。タイトル変えます。

んじゃ、どうすればいいの? JavaMailを使いましょう。

import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Mail
    public static void main(String args[]) {
      try {
          String makeFromText = "テスト様";
          String fromText = encodeFromString(makeFromText, "spoofing@gmail.com"); // なりすましのメール
          sendMail(
            "件名",
            fromText,
            "to@gmail.com",
            "mail body"
          );
      } catch (Exception $e) {
        // ...
      }
    }

    private static void sendMail(String subject, String from, String to, String body) throws Exception {
        // 定数は任意で設定ください
        String contentType = "text/plain; charset=UTF-8";

        Properties props   = new Properties();

        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.host", HOST);
        props.put("mail.smtp.port", PORT);
        props.put("mail.smtp.from", MAIL_LOGIN_ID); // これがSMTP Authに使うfrom

        Session mailSession = Session.getInstance(props);
        mailSession.setDebug(true);

        MimeMessage message = new MimeMessage(mailSession);
        message.addFrom(InternetAddress.parse(from)); // mail spoofing
        message.setRecipients(Message.RecipientType.TO, to);
        message.setSubject(subject);
        message.setContent(body, contentType);
        message.setHeader("Content-Transfer-Encoding", "7bit"); // 最初文字化けしたけど、この行を追加したらいけた

        Transport transport = mailSession.getTransport();
        try{
            System.out.println("Sending ....");
            transport.connect(HOST, PORT, FROM_MAILADDRESS, PASSWORD);
            transport.sendMessage(message,message.getRecipients(Message.RecipientType.TO));
            System.out.println("Sending done ...");
        }
        catch(Exception e) {
            System.err.println("Error Sending: ");
            e.printStackTrace();
        }
        transport.close();
    }
}

コメントにも書いていますが、PASSWORDなどの定数は任意で設定ください。

ちなみにGmailの場合は、Session mailSession = Session.getInstance(props);の箇所を下記に拡張する必要がある。

        Session mailSession = Session.getInstance(props,
            new javax.mail.Authenticator() {
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(FROM_MAILADDRESS, PASSWORD);
                }
            }
        );

これでうまくいきましたー!やったーー!

ちなみに、最初↑のコードをどっからかパクってきて、動作させようとしましたが、いまいちピンと来ず、混乱していました。

頭を整理するためにも質問サイトで質問事項を書いて、何がわからないのかを質問してみました。

stackoverflow.com

汚い英語だと思いますが、返事が返ってくる! 何故かコメントで。。。(どうアクションすれば良いのだ)。あとみんなやさしいいい。うれしい。

I believe mail.smtp.from is the envelope MAIL FROM

↑は、mail.smtp.fromエンベロープの"MAIL FROM"と言っているのかな? というかメールセッションの中に"from"を設定するところがあるじゃん!

ってことで、コメントと逆の発想でmail.smtp.fromに、メールサーバにログインするときに使うMAIL FROMのIDを記述。

んで、おそらくエンベロープの"from"はmessage.addFrom(InternetAddress.parse(from)); // mail spoofing←ここでしょ。ってことで、ここに記述。

そしたらでキタ━━━━(゚∀゚)━━━━!!

コードはちゃんと読もう&あとでコメントに感謝して解決内容を投稿しておきます。