なぜクレジットカード番号やセキュリティコードが流出するのか

【スポンサーリンク】

商品をクレジットカードで購入できるサイト(いわゆるネットショップ)には、当然、購入者の氏名や住所、メールアドレスなどの個人情報や、購入した商品の種類や個数、価格などの購入情報がデータベースに保存されていることが想像できます。しかし、普通はクレジットカード情報(クレジットカード番号、名義人、有効期限、セキュリティコードなど)はそのネットショップのデータベースには保存されていません。たとえ「次回購入のためにクレジットカード情報を保存する」のような機能を利用したとしても、そのネットショップのデータベースには保存されません。なぜなら、購入者が入力したクレジットカード情報は、ネットショップからそのままクレジットカード決済代行会社に送信されて決済が実施されるためです。クレジットカード決済代行会社は決済を実施したり、クレジットカード情報を保存するためのAPIを用意しています。ネットショップはそれらのAPIを利用すればよいので、クレジットカード情報を自社のデータベースに保存する必要がないのです。

にもかかわらず、世の中の個人情報流出のニュースを見ていると、氏名や住所やメールアドレスなどに加えてクレジットカード番号やセキュリティコードまで流出している例が見られます。このような事件が起きるたびに、SNSなどでは「なぜクレジットカード番号やセキュリティコードまで保存する仕様にしたのか」といった意見が聞かれますが、前述のような仕組みであれば、あえてクレジットカード情報を保存する仕様にしているとは考えにくいでしょう。では、なぜクレジットカード番号やセキュリティコードが流出してしまうのでしょうか。ありがちな「ログに残ってしまった」例を考えてみます。

※なお、ログに残る以外にも、クレジットカード情報入力画面が改ざんされて入力値が収集されてしまう手法なども考えられます。

決済時のログを記録している

バグの調査や購入者からの問い合わせに対応するために、決済時の情報をログとして記録している場合があるかと思います。以下はPHPの例です。

<?php
$params = array();
$params['card_number'] = $_POST['card_number'];
$params['card_holder'] = $_POST['card_holder'];
$params['card_expire'] = $_POST['card_expire'];
$params['card_securitycode'] = $_POST['card_security_code'];
$params['order_number'] = 注文番号;
$params['order_price'] = 注文金額;

// 決済代行会社のAPI呼び出し
$api->authorization($params);

// 後の調査のために決済時のパラメータをログに記録する
error_log(print_r($params, true), 3, 'log.txt');

この場合、当然log.txtが外部に流出するとクレジットカード情報が流出することになります。

エラーハンドラでログを記録している

決済時のログを明示的に記録していなくても、エラー時に自動的にログを記録する仕組みがあります。以下はPHPのエラーハンドラの例です。

<?php
set_error_handler(function($errno, $errstr, $errfile, $errline){
    // 後の調査のためにバックトレースをログに記録する
    error_log(print_r(debug_backtrace(), true), 3, 'log.txt');

    return true;
});

$params = array();
$params['card_number'] = $_POST['card_number'];
...

// 決済代行会社のAPI呼び出し
$api->authorization($params);

この場合、正常に処理が完了した場合は問題ありませんが、何かエラー(WarningやNoticeなども含む)が起きるとその内容が自動的にログに記録されます。バックトレースにはPOSTされた値などが含まれる可能性があるので、ログにクレジットカード情報が記録されてしまいます。

対策

クレジットカード情報をマスキングしてログに記録する

クレジットカード情報が記録されたログが流出してしまう場合を想定して、クレジットカード番号やセキュリティコードを ***** のような文字列に置換したり、消去したりしてログに記録するという方法が考えられます。

<?php
$params = array();
$params['card_number'] = $_POST['card_number'];
...

// 決済代行会社のAPI呼び出し
$api->authorization($params);

// 後の調査のために決済時のパラメータをログに記録する
unset($params['card_number']);
...
error_log(print_r($params, true), 3, 'log.txt');

ただし、この方法ではマスキングし忘れたり、エラーハンドラを利用する場合は正規表現などでマスキングする情報を特定しなければならず、対策が漏れやすい方法と言えます。

トークンによるクレジットカード決済に対応する

そもそも、クレジットカード情報をネットショップに送信しなければログにも残りません。経済産業省は「クレジットカード取引におけるセキュリティ対策の強化に向けた実行計画2017(「実行計画2017」)」というものを策定しており、この計画でネットショップはクレジットカード情報を保存することのみならず、「通過」させないことも強く推奨してます。つまり、クレジットカード情報をネットショップにGETPOSTで送信することすら危険なのでやめましょうということです。 www.meti.go.jp ではクレジットカード情報をネットショップに送信せずにどのように決済を実施するのかということになりますが、そこで登場するのが「トークン」です。購入者が入力したクレジットカード情報をJavaScriptによってトークン化し、そのトークンをネットショップからクレジットカード決済代行会社に送信することによって決済を実施します。

function pay() {
    getToken({
        'card_number': $('#card_number').val(),
        'card_holder': $('#card_holder').val(),
        ...
    }, callback);
}

function callback(response) {
    // トークンを取得する
    $('#card_token').val(response.token);

    // カード情報をサーバに送信しないように値をカラにする
    $('#card_number').val('');
    $('#card_holdeer').val('');
}

ネットショップのクレジットカード情報入力画面のHTMLソースをよく見ると、token.jsのようなJavaScriptファイルが読み込まれていることがあります。これはクレジットカード決済代行会社が提供しているトークン化のためのライブラリの可能性があります。

このように、クレジットカード情報を自社のデータベースに保存する仕様は論外ですが、それ以外にもログを通してクレジットカード情報が流出してしまう危険性があるため、ネットショップはトークンに対応したクレジットカード決済を導入することが強く推奨されています。購入者の立場からすると、トークンに対応していることが明示されていると安心度が増すのではないでしょうか。