web-dev-qa-db-ja.com

SESSIONにクレジットカード番号を保存する-それを回避する方法は?

私はPCIコンプライアンスをよく知っているので、チェックアウトプロセス中にCC番号(特にCVV番号)を会社のデータベースに保存することについては、耳を傾ける必要はありません。

ただし、消費者の機密情報を処理するときはできるだけ安全にしたいと考えており、可能な限りSESSION変数を使用せずにCC番号をページからページに渡す方法に興味があります。

私のサイトは次のように構築されています。

  1. ステップ1)顧客からクレジットカード情報を収集します-顧客が送信を押すと、情報は最初にJS検証を実行し、次にPHP検証を実行します。すべて合格した場合、ステップ2に進みます。
  2. ステップ2)顧客のレビューページに情報が表示され、今後の取引の詳細が表示されていることを確認します。このページにはCCの最初の6つと最後の4つだけが表示されますが、カードの種類と有効期限は完全に表示されます。彼が[続行]をクリックすると、
  3. ステップ3)情報は、最後の検証を実行し、安全な支払いゲートウェイを介して情報を送信する別のphpページに送信され、文字列が詳細とともに返されます。
  4. ステップ4)すべてが順調である場合、消費者情報(CCではなく個人)がDBに保存され、完了ページにリダイレクトされます。何か悪いことがあれば、彼は通知され、CC処理ページに戻って再試行するように指示されます(最大3回)。

助言がありますか?

編集

私はこの質問に対して多くの本当に良い回答を受け取りました-大多数は次のことに同意しているようです:

  1. 検証の実行後にPOST変数を取得
  2. ccnumとcvvの暗号化(ただし、cvvをDBに保存できるかどうかはわかりません)
  3. 一時DBへの保存
  4. 「レビュー」ページがOKになった直後にDBにアクセスする
  5. dBから詳細を復号化
  6. プロセッサに情報を送信する
  7. 応答を受け取る
  8. dBを終了します

これは全体的に理にかなっていると思います。後で呼び出すときに自動的に削除される一時的なDB情報を作成するための最良の方法とともに、暗号化/復号化のための優れた方法を誰かが持っていますか?

私はPHPとMySQLDBでプログラミングしています

編集#2

私は理想的な解決策のように思われるPacketGeneralに出くわしましたが、この目標を達成するために別のソフトウェアライセンスにお金を払いたくありません。 http://www.packetgeneral.com/pcigeneralformysql.html

編集#3-サンプルコード

この投稿で言及されている暗号化/復号化/キーとストレージを理解するためにまとめたサンプルコードをいくつか投稿しました。うまくいけば、すでに役立つ貢献者が検証でき、他の人も同様の機能を使用できるようになります。長さのために、実際のCC番号自体に使用される検証方法については説明しません。

フォーム入力

<form action="<?php $_SERVER['PHP_SELF']; ?>" method="POST">
<input type="text" name="CC" />
<input type="text" name="CVV" />
<input type="text" name="CardType" />
<input type="text" name="NameOnCard" />
<input type="submit" name="submit" value="submit" />
</form>

PHPの暗号化とデータの保存

<?php

$ivs = mcrypt_get_iv_size(MCRYPT_DES,MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivs,MCRYPT_Rand);
$key = "1234"; //not sure what best way to generate this is!
$_SESSION['key'] = $key;

$ccnum = $_POST['CC'];
$cvv = $_POST['CVV'];
$cctype = $_POST['CardType'];
$ccname = $_POST['NameOnCard'];

$enc_cc = mcrypt_encrypt(MCRYPT_DES, $key, $ccnum, MCRYPT_MODE_CBC, $iv);
$enc_cvv = mcrypt_encrypt(MCRYPT_DES, $key, $cvv, MCRYPT_MODE_CBC, $iv);
$enc_cctype = mcrypt_encrypt(MCRYPT_DES, $key, $cctype, MCRYPT_MODE_CBC, $iv);
$enc_ccname = mcrypt_encrypt(MCRYPT_DES, $key, $ccname, MCRYPT_MODE_CBC, $iv);


//if we want to change BIN info to HEXIDECIMAL
// bin2hex($enc_cc)

$conn = mysql_connect("localhost", "username", "password");
mysql_select_db("DBName",$conn);

$enc_cc = mysql_real_escape_string($enc_cc);
$enc_cvv = mysql_real_escape_string($enc_cvv);
$enc_cctype = mysql_real_escape_string($enc_cctype); 
$enc_ccname = mysql_real_escape_string($enc_ccname);

$sql = "INSERT INTO tablename VALUES ('$enc_cc', '$enc_cvv', '$enc_cctype', '$enc_ccname');

$result = mysql_query($sql, $conn) or die(mysql_error());
mysql_close($conn);

Header ("Location: review_page.php");

?>

PHPによるデータの復号化とゲートウェイへの送信

    $conn = mysql_connect("localhost", "username", "password");
    mysql_select_db("DBName",$conn);

$result = mysql_query("SELECT * FROM tablename");

echo mcrypt_decrypt (MCRYPT_DES, $_SESSION['key'], $enc_ccnum, MCRYPT_MODE_CBC, $iv);
echo mcrypt_decrypt (MCRYPT_DES, $_SESSION['key'], $enc_cvv, MCRYPT_MODE_CBC, $iv);
echo mcrypt_decrypt (MCRYPT_DES, $_SESSION['key'], $enc_cctype, MCRYPT_MODE_CBC, $iv);
echo mcrypt_decrypt (MCRYPT_DES, $_SESSION['key'], $enc_ccname, MCRYPT_MODE_CBC, $iv);

mysql_close($con);
?>

次に、文字列で送信されたばかりのデータを取得し、ゲートウェイ送信で使用します。そうですか?

38
JM4

クレジットカード情報を保存する必要性をなくすために、チェックアウトプロセスを変更することを検討してください。

ページ1:ユーザーが配送先住所や請求先住所などのクレジットカード以外の注文情報を入力する
ページ2:ユーザーは、クレジットカード以外の注文情報を確認し、クレジットカード情報を入力して、[今すぐ支払う](​​または変更する場合は[注文を修正])をクリックします。
ステップ3:情報は$ _POSTリクエストを介してSSLページに送信されます。SSLページはサーバー側のチェックを完了し、クレジットカードデータをプロセッサに送信し、応答に基づいてユーザーを成功またはエラーページに誘導します。

このようにして、技術的な問題やコンプライアンスの問題のかすみを回避できます。クレジットカードデータをデータベースまたはCookieに保存することは、たとえ暗号化されていても、短期間であっても、より高いレベルのPCIコンプライアンスに責任があることを意味します。唯一のトレードオフは、クレジットカードの詳細を含む「注文の確認」ページを表示できないことです。また、「注文の確認」ページにクレジットカード番号全体を表示することすらできないことを考えると、トレードオフはどれほど大きいのでしょうか。

9
Summer

カードの詳細を任意の永続メディア(データベースなど)に保存しますが、セッションに保存する一意のおよびランダムキーを使用してカード番号を暗号化します。そうすれば、セッションが失われた場合、キーも重要になります。これにより、期限切れ/放棄されたデータをクリーンアップするのに十分な時間が与えられます。

また、セッションがハイジャックから保護されていることを確認してください。これにはハードウェアソリューションがありますが、コード内の簡単な方法は、セッションIDをIPの最初のオクテットとユーザーエージェントのハッシュに関連付けることです。絶対確実ではありませんが、役に立ちます。

編集:リスクを最小限に抑えるための重要な点は、その情報をできるだけ早く取り除くことです。トランザクションが完了した直後に、データベースからレコードを削除します。また、セッションタイムアウト(通常は20分)より古いレコードを削除するローリングジョブ(たとえば5分ごと)も必要です。また、このvery一時データにデータベースを使用している場合は、自動バックアップシステム上にないことを確認してください。

繰り返しになりますが、このソリューションは絶対確実ではなく、CCセキュリティ要件に準拠しているとは100%確信していません。ただし、攻撃者が顧客のCC情報をアクティブに復号化するには、環境を完全にランタイム制御する必要があります。データベースのスナップショットが危険にさらされた場合(はるかに可能性が高い/一般的)、一度にブルートフォースできるCCは1つだけです。これはあなたが望むことができる最高のものです。

13
Rex M

PCIコンプライアンスを認識しているとのことですが、すでに説明した方法のいずれかを使用すると(たとえば、カード番号をどこでもディスクに保持する)、PCIに違反し、コンプライアンスの悪夢が目の前にあることを意味します。カード番号をディスクに保持することを本当に主張している場合は、PCI監査人を雇って、プロセスを支援し、アドバイスを提供することをお勧めします。最終的に、彼らはあなたが取った方法が適切であることを検証する必要があります。

例として、ここでの回答の多くは暗号化の使用について説明しています。それは簡単なことです。彼らはキー管理について話していません 大幅により難しい

したがって、カードの詳細が収集されたらすぐに支払いゲートウェイに送信する方がよいと思います。多くのペイメントゲートウェイでは、「ストアのみ」スタイルのトランザクションを実行できます。これにより、カードの詳細の基本的な検証が実行され、カード番号が(すでにPCI準拠の)サーバーに保存され、代わりにトークンIDが返されます。この方法は、完全なカード番号/ cvv2をサーバーのどこにも保存しないことを意味し、PCIコンプライアンスは巨大な量になりやすくなります。

チェックアウトプロセスの後半で、トークンIDを使用して承認と決済を送信します。

PCIを使用すると、カード番号の最初の6桁/最後の4桁(および有効期限)をプレーンテキストで保存できるため、快適な場所に安全にキャプチャして、最終ステップの直前に再表示できます。

9
PaulG

確認手順をスキップしてすぐに取引を送信できない理由はありますか?

データベースに保持する方がセッション変数に保持するよりも安全である理由がわかりません。サーバーの侵害によってクレジットカード番号が提供されますが、セッションに保持すると、次のように書き込まれる可能性がはるかに低くなります。ディスク。必要に応じて暗号化できますが、これの有用性は疑わしいです(ディスクにスワップされます)。暗号化されたストレージを実行するために別のマシンを追加しても、侵害されたマシンは他のマシンに復号化を要求するだけなので、役に立ちません。

編集:これについて考えてみてください:

  1. ランダムな128ビットキーを生成します。これをセッションに保存します。
  2. キーを使用してデータを暗号化します。 <input type = "hidden">のclientに送信します
  3. 確認したら、データを復号化し、トランザクションを送信します。

攻撃者は、クレジットカード番号を取得するために、クライアントとサーバーの両方を危険にさらす必要があります(このような攻撃者は、おそらくすでに番号を持っているでしょう)。オンラインサーバーの侵害は、今後のトランザクションのクレジットカード番号を取得しますが、それを止めることはできません。

編集:そして私は詳細を忘れました。これらすべてのスキーム(私のものだけでなく)では、リプレイ攻撃を防ぐためにMACも必要です(または、イブがアリスの気をそらし、買い物かごと請求先住所を変更して、「確認」ページを押します...)。一般に、所有しているすべてのトランザクションデータ(CC、CVV、トランザクションID、トランザクション金額、請求先住所など)にMACを設定する必要があります。

6
tc.

そうです、sessionsを使用すると、機密データを保存するのに非常に安全ではありません。次のように知られているセッションに侵入する方法があります。

セッションハイジャック
セッション固定

私の頭に浮かぶ最も安全な方法は、情報をデータベースに(一時的に)保存してから、必要なページでその値を読み取ることです。すべての作業が終了したら、削除して戻すことができます。

注意:

  • データベースに保存する前に、暗号化する必要があります
  • 共有ホスティングを使用している場合は注意してください
  • 完了したら、必ずdelete元に戻してください

this反射的にも役立つことがあります:)

4
Web Logic

とにかくそれに触れると、安全なクレジットカード番号の保存機能を提供する必要があるようです。サーバーが危険にさらされた場合、いつでも、現在保存されているクレジットカード番号(つまり、キーと暗号化された番号)を復号化するのに十分な情報が含まれます。考えられる解決策は、「暗号化/復号化」サービスとして機能する内部サーバーを使用することです。このように1台のマシンを危険にさらしても、クレジットカード番号は公開されません。

3
Greg Adamski

別の方法がありますが、Ajaxが必要です。クレジットカード番号とレビューページの保存はありません。

ページ1:配送、請求、クレジットカード情報を取得するためのフォーム。フォームを含むページの「本文」が、JavaScriptで参照できるように、一意のIDを持つDIV内にあることを確認してください。

ページ2:フォームフィールドを含むGET/POSTリクエストを受け入れ、適切にフォーマットされた「レビュー」ページを好みに戻すサーバー上のファイル。

チェックアウトプロセス:

  1. フォームを検証します。
  2. クレジットカード関連のフィールドをグローバルJavaScript変数にコピーします。
  3. フォームフィールドをループし、フォームフィールド(クレジットカード関連のフィールドを除く)を使用してクエリ/データ文字列を作成します
  4. 「レビュー」ページにAjaxリクエストを実行し、フォームフィールド/値のクエリ文字列を渡します。サーバーでレンダリングし、Ajax関数の呼び出しに戻ります。
  5. Ajaxリクエストから返されたレンダリングされたHTMLレビューページを取得し、「DIV」コンテナ内のコンテンツをそれで置き換えます(フォームやその他の要素をレビューHTMLで効果的に置き換えます)。
  6. JavaScriptを使用して、グローバルJS変数に保存されているクレジットカードデータをレビューページの適切な場所にコピーします。カードデータを非表示のフォームフィールドにコピーして、ユーザーが「レビュー」ページから注文を「完了」したときに送信することもできます。
  7. ユーザーはレビューページからサーバーに注文を送信し、プロセッサのゲートウェイでカードの検証を実行してから、注文を行うか、カードの詳細を保存せずにエラー処理ページに戻ります。
  8. グローバルJS変数にカードデータが保存されなくなったページをブラウザにリロードするために、「注文する」関数で(Ajaxではなく)完全なHTTPリクエストを実行することをお勧めします。

ちょっとしたハックですが、適切に実行すると、ユーザーにとって100%シームレスになり、一時的なDBの保存などのリスクを負うことなく、カードデータを1回送信できます。

3
Burgertech

これが私がそれを行うことを計画している方法です-もちろんsslを使用してhttps経由ですべて。

手順1.ユーザーがCC情報を入力し、次のボタンを押します。 CC情報は、自分のDBではなくCCプロセッサのDBにすぐに保存されます。そうしないと、PCIコンプライアンスに違反することになります。 CCは、このステップで実際に課金されることはありません。ただし、CC情報を識別するプロセッサから一意のIDを受け取る必要があります。 (必要に応じて、一意のIDをデータベースに保存します)

手順2.確認ページで、指定された一意のIDを使用してCCプロセッサからCC情報を取得します。私のプロセッサでは、とにかくCCの最後の4つの番号しか取得できません。

ステップ3.購入を確認し、[今すぐ購入]ボタンを押したら、一意のIDを使用してクレジットカードに請求します。

ステップ4.CCの最後の4桁しかない請求書/領収書を含むありがとうページにリダイレクトします(もちろん、CCの最後の4桁を表示する必要はありませんが、表示するのは良いことだと思います)。

2
goku_da_master

これがデータベースの目的です。ここでの法的な影響(国や地域によって異なります)についてはわかりませんが、1つのアプローチは、CC番号を暗号化し、ユーザーから受け取ったらすぐにデータベースに保存することです。必要なときにユーザーに表示できるように、最後の4桁を別のフィールドに保存することをお勧めします。サーバー上のカードプロセッサと対話する必要がある場合は、データベースからカード番号を取得して復号化します。

2
3Dave

支払い処理の後のある時点(ステップ3の最後の部分)で、CC#(およびCVC)を暗号化して、支払い処理業者に送信できるようにする必要があります(私は推測します)

確認ページに必要な難読化の横で、情報を受け取ったときにその暗号化を実行してみませんか。 (これはステップ1の最後の部分です)

今後は、この暗号化または難読化されたデータのみを処理し、CC会社を実際に完全なデータを復号化できる唯一の会社にします。

1
alexanderpas

情報を保持するためのセッションやデータベースは必要ありません。

すべてのページは、データを投稿するフォームです。後続の各ページで、前のページの投稿変数が非表示のフォームフィールドに追加され、次のフォーム送信でデータが再度投稿されるようになります。この方法では何も保存されませんが、情報はページからページへと運ばれます。これにより、ユーザーは手順をスキップせずに、最初から最後までプロセスを完了する必要があります。

フォームがHTTPS経由で送信される限り、データは自動的に暗号化され、セキュリティの負担はSSL証明書プロバイダーにあります。

多くの人気のあるコマースサイトがこれを実装しています。たとえば、OSCommerce。

1
None

カードnrのハッシュをセッションに保存し、同じハッシュと実際の番号およびユーザーのセッションIDをデータベースに保存できます。次に、ページごとにハッシュとセッション情報を確認して、カード番号を取得できます。

1
Ólafur Waage

トークン化を使用します。その完全なPCI DSS準拠。

詳細については、こちらをご覧ください https://www.pcisecuritystandards.org/documents/Tokenization_Guidelines_Info_Supplement.pdf

0
Phpcian