Help us understand the problem. What is going on with this article?

セブンペイ・PayPayから学ぶ認証セキュリティ

消費税が10%となりポイント還元のキャッシュレス決済が注目され流行っていますが、キャッシュレス決済サービスではこれまで度々大規模なセキュリティに関する事故が発生しています。
ということで、最近発生した金融サービスのセキュリティ事故や様々なサービスがどのようにセキュリティ対策をしているかを参考に、認証系のセキュリティ対策について考えていきたいと思います。

当記事は参考とさせていただくサービスを批判するものではなく、発生した事象を今後に生かそうという趣旨の記事ですのでご理解いただければと思います。

認証以外のセキュリティについてはこちらをどうぞ
Webアプリケーションのメジャーな脆弱性とその対策

PayPay

PayPayでは2018年12月4日~13日の期間で「100億円あげちゃうキャンペーン」と題してPayPayでの支払いが20%還元されるというイベントが行われました。
問題が発生したのはこの期間中で、二重決済や身に覚えのない請求などが発生しました。

認証の試行回数制限

まず最初に注目したいのは、クレジットカードの登録画面でセキュリティコードが何回でも入力できたということです。

画面イメージ
img_flow_add_credit_01.png
画像引用 PayPay->使い方->PayPay残高のチャージから支払う

このようなクレジットカードの登録画面で、セキュリティコードが何回でも入力可能な状態となっていたため、他人のクレジットカード番号さえ分かっていれば登録できてしまう可能性が高い状態であったというのが当時の事象となります。

この事象はクレジットカードの登録に関するものでしたが、一般的なWebアプリケーションにおいても同じです。
もしログイン画面で何回でもパスワード試行ができる状態であり、ログインIDが分かっているような場合、ブルートフォース攻撃(総当たり攻撃)を繰り返すことで不正ログインできてしまう可能性が高まります。

具体的にどういうことか

前述のクレジットカード登録のセキュリティコードを登録する場合、3桁の数値ということはわかっているため、000、001、002、003・・・のように999まで順番に入力を試せばそのうち必ずクレジットカードの登録ができます。

ログイン画面などの場合にも同様です。
パスワード欄に、aaa、bbb、ccc・・・と入力していきます。

ですが、パスワードの場合は適当な文字の並びを設定するよりも、ある程度利用される可能性の高いパスワードを入力していった方が当たる可能性が高くなります。
例えば、password、welcome、iloveyouなど人間が覚えやすい単語がよく使われます。
これらのよく使われるパスワードを手元に持っていて、1つ1つ試していく攻撃方法が辞書攻撃と呼ばれるものです。

サービス開発者側の対策

サービスの利用者側の対策としては、英数字記号を含めたランダムなものにするなどありますが、サービス開発者側の対策について考えます。

1つはユーザーがパスワードを設定する際の制約を強くすることです。
例えばGoogleでは以下のような制約が設定されています。

  • 安全性が著しく低いパスワード(「password123」など)
  • お使いのアカウントで以前に使用したことがあるパスワード
  • スペースで開始または終了するパスワード

引用 Google 安全なパスワードを作成してアカウントのセキュリティを強化する

これ以外でよくあるものとしては、桁数制約(8文字以上など)や文字種類(英数字記号を1つずつ)などがあります。

その他、今回注目したい対策としては、一定回数連続してパスワードを誤った際のアカウントロックがあります。
ちなみにPayPayも事件の後こちらの対策を施しています。

一定回数間違った際のアカウントロック

アカウントロックの方法もいくつかあります。
例えば以下のようなものです。

  • 3回連続で間違えたらアカウントロック(参考:ATM)
  • 5回間違えたら15分間アカウントを一時ロックする。15分経過後ログイン試行可能となる。 ただしアカウント一時ロックを5回行った場合、アカウント永久ロック(参考:iPhone端末)
  • ログイン試行の直前3分の間に、ログインを5回間違っている場合、ログイン不可。 5回の間違いの最初のミスが3分経過したら1回分ログイン試行可能。

これらのアカウントロック方針は、サービスがどのくらい重要なものかによって変わります。

例えばインターネットバンキングシステムなどの金融サービスにおいては、顧客の資産に直接関わるもので影響が大きいことから、5回間違えたら即アカウントロックするかもしれません。
しかしレシピサイトのWebサービスなどでは、ログインできたとしても影響が少ないかもしれません。
そのような場合は、5回間違えて即アカウントロックだとユーザービリティが悪いと感じる可能性があるので、10回間違えたら10分間アカウントをロックするなど、サービス内容などに応じて柔軟に考える必要があります。

ただしお金を扱うサービスじゃないからといってブルートフォース攻撃の対策をしなくてよいわけではありません。
色々なWebサイトで同じパスワードを使いまわしている人が多くいるので、1つのWebサイトでパスワードが判明してしまうと色々なサイトにログインされる可能性があります。
それぞれのWebサイトで対策を講じていく必要があると考えます。

リバースブルートフォース攻撃(おまけ)

ブルートフォース攻撃では、1つのアカウントに対して用意したいくつかのパスワードを試していくという手法でしたが、パスワードは同じものを利用してユーザーIDを変えながらログイン試行をしていく攻撃をリバースブルートフォース攻撃と言います。

ブルートフォース.png

先ほどのアカウントロックの方式では1つのアカウントに対して守ることしかできず、この攻撃を防ぐことはできません。

リバースブルートフォース攻撃への対策としては、ログインを多数試行している人がいないかを監視して、もしログイン失敗が続いているIPが存在した場合にはそのIPを遮断するなどの対応が必要です。

私が経験したものでは、中国からのアクセスで複数アカウントに対して多数のログイン失敗しているものがありました。
ログイン試行のIPに対しては遮断対応を行ったのですが、今度はIPを次々と変えて試行している様子でした。
当時は都度都度IP遮断を行って対策を行ったのですが、そのような攻撃に対してどのような対策がよ
いのかは今でもよくわかっていません。1
一つ思うこととしては、ログインIDをemailなどの単純なものではなく、ランダムなUIDを独自に作成して、そもそもユーザーIDをわかりにくくすることが上記の対策になるのかもしれません。

インターネットバンキングではIDがそのようなものになっていると思いますが、ID自体を漏れにくくするためのものなのだろうと思います。

取引金額制限

もう一つ当時のPayPayの対策としてはクレジットカード利用時の上限額設定を行っています。2

  • 30日間以内5万円に変更
  • 24時間以内2万円の上限追加

こちらは金融サービスでは一般的な不正対策です。

例えば銀行では、ATMでのキャッシュカードの1日あたり限度額が設定されています。
みずほ銀行の場合、キャッシュカードを利用した1日あたりの振込限度額は50~100万円程度に設定されています。
インターネットバンキングでも同様に50万程度の振込限度額が設定されています。3

みずほ銀行->みずほダイレクト->ご利用限度額(お振込・お振替)
みずほ銀行->みずほ銀行キャッシュカードの1日あたりのATMご利用限度額

セブンペイ

セブンペイは2019年7月1日にサービスが開始され、2019年7月2日には不正な取引に関する問い合わせがあり、2019年7月31日の段階では808人/38,615,473 円の被害が確認されています。
結果として2019年9月30日にはサービス廃止となりました。

7pay(セブンペイ)」 サービス廃止のお知らせとこれまでの経緯、今後の対応に関する説明について

セブンペイでは不正アクセスからの不正利用が問題になりました。
株式会社セブン&アイ・ホールディングスにて報告された手口としては、1つはPayPayの内容でも取り上げたブルートフォース攻撃(総当たり攻撃)になります。
それ以外で今回注目したい内容が、パスワード再発行二段階認証です。

パスワード再発行

セブンペイでは、パスワードを忘れた際に利用するパスワード再発行処理にて脆弱性がありました。

パスワード再発行画面には、送付先メールアドレスの入力欄が用意されていました。
そのため、セブンペイを利用しているアカウントのメールアドレスと生年月日さえ分かれば、攻撃者のメールアドレスへパスワード再発行のURLを送って、アカウントハッキングができる状況でした。

パスワードを無断変更.png

パスワード再発行処理では、登録されているメールアドレス以外にパスワード再発行に関するメールを送るべきではありません。

またセブンペイに関するものではありませんが、再発行URLを生成する場合は生成されたURLが推測できるものであってはいけません。
自分たちで考えたロジックだと推測されてしまうリスクがあるので、一般的に公開されていて強い衝突耐性があるものを利用すべきです。
具体的にはUUID version4などのロジックを利用してトークンを生成します。
色々な言語でUUIDを生成するAPIが用意されていると思います。
例えば、Javaの場合はUUID.randomUUID()を利用して生成します。

参考 Java doc->クラスUUID

また、ログイン画面同様にブルートフォース攻撃をされるリスクがあるので、パスワード再設定処理に関しても回数制限処理を入れた方が良いと考えます。

様々なパスワード再設定

パスワード再設定画面には様々なものがあります。

  1. 管理者画面からのパスワード再設定
  2. パスワード変更画面のURLをメールで送る
  3. 仮パスワードをメールで送ってパスワード画面に入力させる

2がセブンペイのパスワード発行と同じものです。
これが一番多く利用されているかなと思います。

1の管理者画面は、コールセンターや郵送、店舗へのパスワード変更依頼があった際に利用されます。
インターネットバンキングでは、パスワード変更画面が用意されておらず店頭などで本人確認したうえで、パスワード再設定するみたいなこともあります。
パスワード再設定画面は脆弱性を作ってしまう可能性があるので、場合によっては有効だと思います。
が、一般的なアプリケーションではユーザビリティが下がってしまうので、サービスの重要度によってどうするか考えるべきだと思います。

その他、現在登録されているパスワードをそのままメールで送るパターンもたまにありますが、そのようなサービスはパスワードを平文もしくは復号化可能な状態で保存しているので信頼ができないです。

二段階認証

セブンペイでもう一つ問題視されていたのは二段階認証です。

二段階認証とは、ログインID/パスワードを利用した認証とは別に認証をさせて、不正アクセス等を防ぐためのものです。

例えばPayPayであれば登録済みの端末以外からのログインが発生した場合には、登録済みの端末へSMS認証用の認証コードが送られて、それを入力しないとログインできないようになっています。

セブンペイでは二段階認証が設定されていなかったので、他の端末からでもログインが可能な状態となり、残高を利用されてしまうというのが今回の問題となりました。

様々な二段階認証

せっかくなので色々な二段階認証について紹介します。

  • SMS認証
    携帯のショートメッセージに認証コードが届いてそれを入力させる認証方式
  • 端末認証
    利用端末を登録させて、それ以外の端末からはアクセスを受け入れない認証方式
  • 乱数表
    インターネットバンキングの振込などの際に利用される暗号カードのことです。
    インターネットバンキングの登録時に郵送で送られてきて、そのカードの指定の場所を画面上で入力箇所を指定させて、振込などの取引をさせるものです。
    2_img06.gif
    画像引用 三井住友銀行->暗証番号・暗証カード

  • MFA認証
    AWSでも利用している方も多いMFA(Multi-Factor Authentication)認証です。
    似たものとしては、トークン認証というものもあります。こちらもインターネットバンキングで利用されることがあります。
    パスワードが定期的に変わるので、それを画面上に入力させて一致するかを確認する認証方式になります。
    dispImage.png
    画像引用 みなと銀行->よくある質問->トークンとは何ですか

  • 合言葉認証
    こちらもインターネットバンキングでよく利用されるものです。合言葉を登録させて、ログイン時に合言葉があっているかを確認させて認証します。
    例えば「出身の学校名は?」みたいなものを登録します。

  • 取引パスワード
    利用者側にログインパスワードとは別にパスワードを登録させて認証させるものです。
    セブンペイのクレジットカードを利用したチャージでも利用されていました。
    ログインではなく、何かの取引実行時に入力させることが多いです。

  • メールによるワンタイムパスワード
    SMS認証と似ていますが、メールでワインタイムパスワードを送り、それを画面上に入力させる認証方式です。

二段階認証には様々な方式があり、それぞれ特徴が異なるので、もし利用する場合にはそれぞれを比較したうえでどれが良いか(もしくは組み合わせで利用)を検討して導入するべきです。

パスワードの管理

その他、セブンペイ・PayPayの例に限らず、認証セキュリティで重要なものをピックアップして紹介します。

パスワードの非可逆化

まずはパスワードの管理方法についてです。
パスワード再設定の部分でも軽く触れましたが、パスワードをDBに保存する際には平文、復号化可能な状態で保存してはいけません。

平文は当たり前だと理解できると思うのですが、複合可能な状態で保存しちゃだめというのがどういうことかを説明します。

以下は共通鍵認証(AES256)を利用して暗号化されたテーブルの状態です。

email password
test1@test.com boOyd0Mxk6ELwir67X+vEw==
test2@test.com EeSeOZLyuGhbR850HNTKxA==
test3@test.com 4zISiNI1CNZxZMkxukmXDQ==

攻撃者が何等かの方法で鍵を入手した場合、暗号化は簡単に複合されてしまいます。

$ echo "boOyd0Mxk6ELwir67X+vEw==" | openssl enc -d -aes-256-ecb -d -base64 -pass pass:abc -nosalt
password

なので、非可逆化つまりハッシュ化することが重要です。

email password
test1@test.com 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d
test2@test.com 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
test3@test.com e4ad93ca07acb8d908a3aa41e920ea4f4ef4f26e7f86cf8291c5db289780a5ae
test4@test.com 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d

ハッシュ化されているものは、元の文字列に戻すことは出来ないので万が一パスワードテーブルが漏れたとしても安全です。

しかし、上記テーブル一覧で一つ注目したい箇所があります。
test1@test.comtest4@test.comのパスワードに同じ値が利用されているのが分かります。
このような状態の時、攻撃者はいくつかのパスワードを実際に登録して、暗号化された値同士を比較してパスワードを特定するという攻撃手段があります。
このようなに事前にいくつかのパスワードをハッシュ化しておいて、ハッシュ化された値からパスワードを抽出する手法をレインボーテーブルといいます。

これの対策としては、ソルトストレッチングです。

ソルト

ソルト(salt)はハッシュ化対象の文字列に追加する文字列のことです。
ユーザーごとに異なるソルトを設定することで、同じパスワードであっても別のハッシュ値が計算されるようになります。

$ echo -n 'passwordtest1@test.com' | shasum -a 256
734b33cc1e9f858e71d0755532d8cff899def8729d9dfeeed21b5690a2fd2777  -
$ echo -n 'passwordtest4@test.com' | shasum -a 256
8a5d7442b4a29da691e1924250ba7c3ac5cbee99706406af227606a7e2cb8239  -

上の例では、パスワードにメールアドレスを加えてハッシュ化することでパスワードの値をそれぞれ異なるようにしています。

ストレッチング

ストレッチングは、ハッシュ値の計算を何回か繰り返し行いハッシュ化する方法です。
そうすることで、ハッシュの計算速度をあえて遅くして攻撃にかかる時間を長くすることが可能です。

$ echo -n 'passwordtest1@test.com' | shasum -a 256
734b33cc1e9f858e71d0755532d8cff899def8729d9dfeeed21b5690a2fd2777  -
$ echo -n '734b33cc1e9f858e71d0755532d8cff899def8729d9dfeeed21b5690a2fd2777' | shasum -a 256
668bd724f17fca0654fa1c269b1c3a953d086a4d04daca19dced9b42a3f06afd  -

ちなみにJavaのSpringユーザーであれば、SpringSecurityでStandardPasswordEncoderを利用すれば、ソルトの付与・ストレッチングが簡単に実現可能です。

また、bcryptアルゴリズムなどより計算に時間がかかるハッシュ関数を利用することも推奨されます。
こちらもSpringSecurityの場合は、BCryptPasswordEncoderを利用することで実現可能です。

ログ運用

最後はログに関してです。

まず認証系の処理に関してはGETリクエストをしないことです。
GETリクエストで処理をすると至る所のログに出力してしまいます。

apacheのaccessログ
192.168.0.101 - - [01/oct/2019:19:00:00 +0900] "GET /signup?email=test@test.com&password=password HTTP/1.1" 200 1000 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"

こちらでは、password=passwordとしてリクエストが飛んでいることが分かります。

またPOSTリクエストの場合でも注意が必要です。
アプリケーション側でリクエストされた内容を出力していると、ログに出力されてしまいます。

2019-10-01 19:00:00.707  INFO 1345 --- [nio-8080-exec-5] com.example.demo.LoggingAdvice           : [28C19D6633F7120F888BA3F489BAB6CD]:class com.example.demo.controller.AccountReissueController.resetpassword:START:29c416f8-f3d6-4a66-9da1-e030fb99c131,UserPasswordChangeForm(password=password, passwordConfirmation=password),{userPasswordChangeForm=UserPasswordChangeForm(password=password, passwordConfirmation=password), org.springframework.validation.BindingResult.userPasswordChangeForm=org.springframework.validation.BeanPropertyBindingResult: 0 errors},{},org.springframework.validation.BeanPropertyBindingResult: 0 errors

UserPasswordChangeFormの中身がpassword=password、passwordConfirmation=passwordになっていることが出力されています。

このような場合は、隠したいパラメータをMASK化するようにアプリケーションを修正する必要があります。
パスワードがログに出力されると、内部からの情報へのアクセスが可能になり、攻撃されてしまう可能性があるので、十分に気を付けてログの設計をする必要があります。

ちなみにJavaでlombokなどを利用している場合には、@ToStringアノテーションのexcludeパラメータに除外対象のフィールドを設定することでログへの除外が可能です。

UserPasswordChangeForm.java
@Getter
@AllArgsConstructor
@ToString(exclude={"password", "passwordConfirmation"})
public class UserPasswordChangeForm {
    private String email;
    private String password;
    private String passwordConfirmation;
}

参考文献


  1. このような大量の不正アクセス試行が見られた場合は警察へ連絡を。 

  2. ちなみに現在は少し変わっているみたいです。PayPay->ご利用上限金額について 

  3. 個人で金額の設定が可能です 

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
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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