3Dセキュア認証を初めて体験した感想

技術的な話題ともいえなくないのでこちらに書くことにする。ただし、専門家ではないので細部に渡って内容を検証しているわけではなく、あくまで利用者の視点で書いているということを了承いただきたく。

3Dセキュア認証とはここ (ZeusのWebページ)にも説明がある通り、カードの認証にカード番号、有効期限に加え、当該カード会社のWebサービス上に登録していたパスワードの入力を要求するものだ。

正直言って血の気が引いた。なぜって、パスワードの入力時に一旦カード会社と思しき別のドメインのページにリダイレクトするからだ。

このリダイレクトに関して、ベリトランスのページには次のような説明があった。

(前略)
いずれのサービスも、発行カード会社に事前登録したインターネット専用パスワードを利用して、 購入者がカード所有者本人であるかを、発行カード会社が直接確認・認証することで、 第三者のカード不正使用を防止する仕組みとなっています。 なお、ユーザーにより入力されたパスワードは、クレジットカード発行会社に直接暗号化送信されるため、 ECサイトでは取得できない仕組みになっており、店舗での情報漏洩等による事故も防ぎます。

3Dセキュア(3-D Secure™)はなぜ安全なの?

で、実際に利用してみてどのドメインに転送されるのかと思ったら、わけのわからない securesuitejp.net というあからさまにフィッシングと見まがえそうなページに到達してしまった。

f:id:moriyoshi:20070523073117p:image

これは正直ひどい。

まず、パーソナルメッセージとはなんなのだろうか。パーソナルという名前から、ユーザがここに自由にメッセージを設定できることが想定されていて、ユーザがこのサイトをオーソライズするために使う「共通の秘密」なのだろうと察せられるが、「UFJカードWebサービス本人 認証サービス」などというセクシーでもなんでもないフレーズを設定した覚えは毛頭ない。で、前出のベリトランスのページには次のような説明がある。

※パーソナルメッセージとは
カード発行会社からの正規のパスワード入力画面であることを、カード会員が確認するための確認項目です。カード発行会社により設定の有無があるほか、設定方法も異なります

それって意味ないじゃん!!!!!

気を取り直して、このページの「ヘルプ」をクリックしたところ、ヘルプのダイアログが JavaScript を用いたポップアップで表示された。JavaScript を有効にせよ、という前置きもなく*1である。これだけ怪しければ JavaScript を有効にしないで利用するユーザがいてもおかしくないと思うのだが。しかも、ヘルプの文言がどうしようもなかった。

(前略)



  • Q11 「本人認証サービス」を利用したが、お店から別の方法で支払うように要請された(お店の画面に表示された)。



    「パスワード入力画面」で[キャンセル]ボタンをクリックした場合に起こる可能性があります。もし、UFJカードWebサービスのパスワードを忘れた場合は、お手数ですがUFJカードWebサービスへの再登録をお願いいたします。
    UFJカードWebサービス新規登録はこちら : http://ufjcard.com/weblifeclub/ws.html




  • Q12 「本人認証サービス」を解除したい(利用したくない)のですが?



    UFJカードWebサービスへログイン後、「本人認証サービス(安全なオンラインショッピングをサポート)」メニューにて登録を解除いただけます。
    UFJカードWebサービスのURLはこちら : http://ufjcard.com/weblifeclub/ws.html



securesuitejp.net上にあるヘルプページ

うーん呆れる。しかも http://ufjcard.com/weblifeclub/ws.html などというページは存在しなかった。

追記1: securesuitejp.net の expiry は次のようになってました。6/28ってもうすぐですね。

 Record expires on 28-Jun-2007.
 Record created on 28-Jun-2002.

追記1に追記: 21-Jun-2007 にアップデートされていました。おいおい、担当者大丈夫か? 現時点での expiry は

 Record expires on 28-Jun-2008.
 Record created on 28-Jun-2002.

となっている。
追記2: 結局のところさまざまな問題点があるのだが、ざっと考えて致命的なのは以下だろう。

  • 何のために必要なサービスなのかがユーザに明示されていない。
  • ユーザが望めば利用しなくてもよいサービスであることが、サービス提供者からユーザに明示されていない。
  • securesuitejp.net 上で提供されるインターフェイスは、カード会社のものではなく、カード会社にシステムの一部を提供する組織のものであることが明示されていない。むしろ、「カード会社のもの」であると偽っている説明がある。
  • 正式なサービス名がユーザに伝わっていない (「3Dセキュア認証」「3-D Secure™」「本人認証サービス」etc.)
  • コモンシークレットが意味を成していない。サービスの利用前にコモンシークレットの設定を義務付ければいいのか?そういうものでもないだろう *2

*1:もちろん、前置きがあればいいとか、そのような文言や方策が適切であるといっているわけではない

*2:コモンシークレットを用いるなら、たとえば先月の請求金額など、カード会社の発行したもので、ユーザと事前に共有されている、第三者による推測が比較的難しい内容や、ユーザの誕生日などを合わせて表示するほうが、まだましである (ましという以上のものではないと思った)

シリアルポートを UNIX ドメインソケットに見立てる

追記: socat で stdio を指定すればいいような気がしてきた。

f:id:moriyoshi:20070523220945p:image:left:w320
VMWare には、ゲストOSのシリアルポートを、ホストOSの UNIX ドメインソケットに見立てる機能がある *1。これを有効に活用するためには、現在使用している端末とそのソケットをつなぐプロキシがあればいいのだけど、ちょっと探した限りそのような要求を満たすものが見つからなかったのでざっくり書いてみたのが次の Ruby スクリプト。ツッコミどころ満載な気がするけども、何かの役に立つことを願いつつ。

端末には VT220 互換のものを想定している。F12 を押すとプロキシを終了する。使用するときは sockaddr_un('/tmp/aho') の /tmp/aho を、VMWare のソケットへのパス名に変えてください。

追記: そういえばこのままだとエスケープキー効かなくなるということに後で気づいた。そのうち直す。

追記2: ESC がバッファの最後の文字でなければエスケープシーケンスだとみなすようにしてみた。とりあえずはこれでおk。


require 'socket'
require 'termios'

ci = IO.open(0)
co = IO.open(1)

orig_term_state = Termios.tcgetattr(ci);

term_state = orig_term_state.clone()
term_state.lflag &= ~(Termios::ISIG | Termios::ECHO | Termios::ICANON)
Termios.tcsetattr(ci, Termios::TCSANOW, term_state)

s = Socket.open(Socket::PF_UNIX, Socket::SOCK_STREAM, 0)
s.connect(Socket.sockaddr_un('/tmp/aho'))

outbuf = ''
inbuf = ''
escbuf = ''
state = 0
running = true

while running
    r = IO.select([ci,s],[co,s],nil,1)
    if r[0].include?(s)
        inbuf += s.sysread(16384)
    end
    if r[1].include?(co) and not inbuf.empty?
        co.syswrite(inbuf)
        inbuf = ''
    end
    if r[0].include?(ci)
        buf = ci.sysread(16384)
        p = 0
        while p < buf.length
            case state
            when 0
                i = buf.index(0x1b, p)
                if i != nil and i != buf.length - 1
                    outbuf += buf[p...i]
                    p = i
                    esc_buf = ''
                    state = 1
                else
                    outbuf += buf[p..-1]
                    p = buf.length
                end
            when 1
                if esc_buf.length < 5
                    c = buf.length - p
                    c = 5 - esc_buf.length if c > 5 - esc_buf.length
                    esc_buf += buf[p, c]
                    p += c
                end

                if esc_buf.length >= 5
                    case esc_buf
                    when "\x1b[24~": # VT220 F12
                        running = false
                    end
                    state = 0
                end
            end
        end
    end
    if r[1].include?(s) and not outbuf.empty?
        s.syswrite(outbuf)
        outbuf = ''
    end
end

Termios.tcsetattr(ci, Termios::TCSANOW, orig_term_state)

*1:Named Pipe と書かれているがこれは Windows 上での呼称を意識したもので UNIX 的な FIFO ではない