【Twitterやってます】 koress project | しがく(shigaku) | おかじ(okaji)

2007年9月29日 23:22

Ruby on Railsで携帯からファイルのアップロード(+送信者認証)


こんばんは、Cigar-Cook@本業でのWebサイトリリースを控えて死ぬほど忙しい、です。 今日は都内はありえないぐらいに寒かったです。。。秋ですね。

今回は開発者のために、先日リリースした「ソーシャルQRコードブックマーク - よいさいと(β)」の裏側のお話を書いておきます。

まず初めに

このエントリは、Ruby on RailsMTA(Mail Transfer Agent)の連動によって、メールによるファイルのアップロード機能を実現する方法について説明しています。

まずは背景から

現行、端末から直接ファイルをアップロードすることができる携帯電話の端末はほとんどありません。というか、ケータイ業界が著作権ビジネスをドル箱にして村社会・囲い込み思考・縦割りで食って行こうとしている限りは、input type=fileが使えるようになることは今後しばらく無いでしょう。 そこで、メールにファイルを添付する方法に頼ることになります。これは既にプロフ系サービスやブログサービスで一般的な手法になっていて、アタマの柔らかい若年層には抵抗なく受け入れられています。

メールによるファイルのアップロードについてはいくつかポイントがあります。

送信者の認証が必要

開発者側の視点からすると、添付ファイル付きで届いたメールが、どのユーザが発信したのか認証する必要があります。

メールの性質上、送信者(Fromフィールド)はサルにでも簡単に偽装できるため、Fromを使って認証することはできません。そこで、適当なメールアドレスを生成してユーザに覚えてもらった上で「特定のメールアドレスに送ってくる人は、特定のユーザである」という認証ロジックを使うことになります。

つまり、ユーザごとに一意のメールアドレスを生成してあげれば認証できるわけです。

セキュリティ

上記の認証ロジックには理論的には正しいものの、ソーシャルなセキュリティホールがあります。

例えば、ユーザ名「cigar-cook」さんに cigar-cook@hogehoge.com といったメールアドレスを生成してあげて、このメールアドレスに画像を送るとcigar-cookさんのフォトアルバムに追加されるようなサービスを作ったとします。すると、ほぼ間違いなく、cigar-cook@hogehoge.com には大量のエロサイトの宣伝が他人から送られてくることでしょう。他人が容易に想像できるメールアドレスは、ユーザの認証には不完全です。

この対処方法としては、他人が現実的に想像し得ないメールアドレスを生成してあげればokです。

実現方法

上記を踏まえて、送信者認証つきのメールによるファイルアップロードの仕組みについて説明します。

Ruby on Railsにはacts_as_authenticatedという、ユーザの登録からログイン/認証までが一瞬で実装できる素晴らしいプラグインが用意されています。ユーザの情報はDBに格納され、Railsの素晴らしいActiveRecordフレームワークによって、シームレスにRuby上から扱うことができます。

さらに、メール送配信システムであるPostfixには、仮想(Virtual)のメールアドレスを受け付ける機能があり、データベースに受け付けたい仮想メールアドレスをドーンと格納しておくことができるため、acts_as_authenticatedで使用するユーザ情報のテーブルと簡単に連動することができます。

※以下、Debian Linux (etch) + Apache2.0 + Postfix + Ruby on Rails という環境を例に説明します。

ユーザごとにアップロード用のメールアドレス払い出し

まず、ユーザごとに一意のメールアドレスを生成&格納するためにrailsのacts_as_authenticatedプラグインで生成されたテーブルusersに、upmail_addrというカラムを追加します。また、Postfixの制約上、usersテーブルにはupmail_addr他に、メールを処理するユーザを指定するためのカラムが必要になります。これをupmail_mailboxとしておきます。※詳しくはここらへん

例のごとく、Migrationを使ってカラムを追加します。

% script/generate migration users_upmail_addr
% usersにupmail_addrというカラムを追加するようにmigrationファイルを編集
% rake db:migrate

upmail_addrには、ユーザが新規登録した時に(signupアクションの時に)ランダムなメールアドレスを格納するようにします。upmail_mailboxにはメールを処理したいユーザのメールボックスを適当に指定しておきます。特に理由がない限り、Ruby on RailsでWebサービスを動かしているユーザのメールボックスを指定するのが普通です。

% vi app/controllers/account_controller.rb

例: (MD5ハッシュを使う)
@user = User.new(params[:user])
@user.upmail_addr = Digest::MD5.hexdigest( @user.login + Time.now.to_s ) + '@hogehoge.com'
@user.upmail_mailbox = 'railsuser@hogehoge.com'

Postfixの設定

次に、MTAであるPostfixの設定です。

postfixのmysqlインタフェースが無い場合には、先にインストールしておきます。

% aptitude install postfix-mysql

次に、postfixの設定を編集し、virtualドメインに配送されるメールについては、usersテーブルのupmail_addrカラムをlookupしに行くように設定します。

% vi /etc/postfix/main.cf

virtual_alias_domains = hogehoge.com
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual.cf

% vi /etc/postfix/mysql-virtual.cf
hosts = 127.0.0.1
user = (mysqlのユーザ名)
password = (mysqlのパスワード)
dbname = hogehoge_development (Railsのdatabase.ymlで指定しているDB)
table = users
select_field = upmail_mailbox
where_field = upmail_addr

これでTo:フィールドにユーザを特定できる情報を含んだメールが、Webサービスを動かしているユーザに届くようになりました。

メールソースの解析と送信者認証&データ格納

次に、メールソースを解析して送信者が誰であるかを認証し、画像ファイルを適当な場所に格納するプログラムを書きます。

upmail_mailboxで指定したユーザの.forwardファイルに、解析用のプログラムにメールソースを渡すための記述をします。 mailparser.pl という解析プログラムに渡すなら、以下のようになります。

% vi ~/.forward
"| exec /home/hogehoge/rails/bin/mailparser.pl || exit 75 #hogehoge"

解析用プログラムにはパイプによってメールソースが標準入力として入ってきます。あとは、メールから添付ファイルを抽出して、To:で指定されたユーザごとのディレクトリに格納するなり、煮るなり焼くなりです。

「よいさいと」でのQRコード解析とマイリストへの追加

以下、特に「よいさいと」におけるメールの受信からユーザごとのマイリストにQRコードが蓄積されるまでのメールデータの流れをまとめておきます。

  • SMTPでPostfixにメールが届く
  • Postfixはusersテーブルのupmail_addrカラムをルックアップ(MySQL)
    メールアドレスが存在しなければ、Mailer Daemonとして打ち返す。
    メールアドレスが存在すれば、upmail_mailboxのユーザに送る。
  • Postfixがユーザの.forwardを読む。メール解析用プログラムにメールソースを渡す。
  • メール解析用プログラムは
    • 渡されたメールソースからTo:アドレスを抜き出し、対応するユーザを特定する。
    • メールソースの中から添付された画像を取り出す。
    • QRコードのデコーダが画像を解析してURLを取り出す。
    • 取り出したURLをユーザのマイリストに格納する処理や、タグ付けなどの処理を行う。

メール解析用プログラムは、本来ならrailsのフレームワークでやるのが筋なのでしょうが、某Webサービスのコンテストの締め切りまで時間もなかったので、perlで書いています。~500行ぐらいです。

QRコードのデコーダについてはOpen Source QR Decorderというのを使っています。これはdankogaiさんがデモっていたので、パクりました。 Version 0.8のリリースノートを見ると、0.8からコンパイル済みのコマンドライン用解析ツールが添付されるようになったらしいので、JREさえあればそのまま使えます。

 

以上、ざっくりですが、Ruby on Railsで携帯からファイルをアップロードする方法についての説明でした。

--
関連記事

Railsでソーシャルブックマークを作ってみようか(第2回)
gmailで添付ファイルが自動でアップロードされるしくみ
Railsでファイルをアップロードする方法。他の言語より格段に簡単!
初めてのプログラミング体験まとめ(Ruby on Rails編)

トラックバック

コメントする

※返信が必要な場合にはメールにてお問い合わせください.