在会话中存储信用卡号码 - 围绕它的方式?

时间:2010-05-24 20:43:44

标签: php mysql session credit-card

我非常了解PCI合规性,因此在结账过程中不需要在公司数据库中存储CC号码(特别是CVV号码)。

但是,我希望在处理敏感的消费者信息时尽可能安全,并且好奇如何在不使用SESSION变量的情况下绕过页面传递CC号码。

我的网站以这种方式构建:

  1. 步骤1)领取信用卡 来自客户的信息 - 何时 客户点击提交, 信息首先通过JS运行 验证,然后通过PHP运行 验证,如果所有通过他移动 到第2步。
  2. 步骤2)显示信息 客户要制作的评论页面 确定他们即将到来的细节 交易显示。只有 CC的前6个和后4个是 此页面上显示的是卡片类型, 和exp日期完全是shwon。如果他 点击继续,
  3. 步骤3)将信息发送给 另一个运行最后一个的php页面 验证,发送信息 通过安全支付网关,和 返回带有详细信息的字符串。
  4. 步骤4)如果一切顺利,那么 消费者信息(个人而非 CC)存储在DB中并重定向 到完成页面。如果有的话 不好,他被告知并被告知 重新访问CC处理页面 再试一次(最多3次)。
  5. 有什么建议吗?

    修改

    我在这个问题上得到了很多非常好的回应 - 大多数人似乎同意以下几点:

    1. 之后接受POST变量 验证已运行
    2. 加密ccnum和cvv(不确定 你被允许在数据库中存储cvv 尽管如此)
    3. 存储在临时数据库中
    4. “审核”后立即访问数据库 页面没问题
    5. 解密来自DB的详细信息
    6. 向处理器发送信息
    7. 收到回复
    8. 终止数据库
    9. 我认为这总体上是有道理的。有没有人有好的加密/解密方法以及创建临时数据库信息的最佳方法,这些信息会在以后的通话中自动删除?

      我使用PHP和MySQL DB进行编程

      编辑#2

      我遇到了Packet General,这似乎是一个理想的解决方案,但真的不想支付另一个软件许可证来实现这个目标。 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);
      ?>
      

      然后继续获取刚刚在字符串中发送的数据并在Gateway提交中使用。好像对不对?

13 个答案:

答案 0 :(得分:13)

将卡详细信息存储到任何持久性介质(数据库,无论如何),但使用存储在会话中的唯一和随机密钥加密卡号。这样,如果会话丢失,关键也是 - 这给了你足够的时间来清理过期/放弃的数据。

还要确保您的会话免受劫持。有硬件解决方案,但简单的代码内方法是将会话ID绑定到IP的第一个八位字节和用户代理的散列。不是万无一失,但它有所帮助。

修改: 降低风险的关键是确保尽快摆脱这些信息。事务通过后,立即从数据库中删除记录。您还需要一个滚动作业(比如每5分钟)删除任何早于会话超时的记录(通常为20分钟)。此外,如果您使用数据库存储此非常临时数据,请确保它不在自动备份系统上。

同样,这个解决方案并非万无一失,我甚至不能100%确定它符合CC安全要求。但是,它应该要求攻击者对您的环境进行总运行时控制以主动解密客户CC信息,并且如果数据库的快照受到损害(更可能/更常见),一次只能强制使用一个CC,这是你能想到的最好的。

答案 1 :(得分:9)

我知道您提到您已经了解PCI合规性,但使用已经描述过的任何方法(例如,将卡号保存在任何地方的光盘上)都会违反PCI,并且意味着您面临着令人头疼的合规问题。如果您真的坚持将卡号保存到光盘,那么您现在也可以聘请PCI审核员来帮助您完成整个过程并提供建议。最终,他们需要验证您采用的方法是否恰当。

作为一个例子,这里的很多答案都谈到了使用加密。这很容易。他们没有谈到密钥管理,significantly harder

因此,我认为更好的方法是在收集卡片详情后立即将其提交给付款网关。许多支付网关将允许您执行“仅限商店”式交易,该交易将执行卡详细信息的基本验证并将卡号存储到其(已经符合PCI的)服务器,并返回令牌ID。这种方法意味着您不能在服务器上的任何位置存储完整的卡号/ cvv2,并且PCI合规性变得更容易巨大

稍后在结帐过程中,您使用令牌ID提交授权和结算。

PCI允许您以明文形式存储卡号的前六位/后四位(和失效日期),这样您就可以安全地捕获那些您感觉舒适的位置,以便在最后一步之前重新显示它们

答案 2 :(得分:9)

考虑修改结帐流程,以摆脱存储信用卡信息的必要性。

第1页:用户输入非信用卡订单信息,如运费和账单地址
第2页:用户验证非信用卡订单信息,输入信用卡信息,然后点击“立即付款”(或“修改订单”,如果他们想要更改东西)
步骤3:通过$ _POST请求将信息提交到SSL页面,该页面完成服务器端检查,向处理器提交信用卡数据,并根据响应将用户引导至成功或错误页面。

通过这种方式,您可以避免出现技术问题和合规性问题。将信用卡数据存储在数据库或cookie中,即使在很短的时间内,即使加密,也意味着您要对更高级别的PCI合规性负责。唯一的权衡是您将无法显示包含信用卡详细信息的“审核订单”页面。考虑到您的“审核订单”页面甚至无法显示完整的信用卡号,那么权衡有多大呢?

答案 3 :(得分:6)

您是否有任何理由不能跳过确认步骤并立即提交交易?

我不明白为什么将它保存在数据库中比将其保存在会话变量中更安全 - 服务器泄密仍然会泄露信用卡号,但是如果你将它保留在会话中,它就不太可能被写入磁盘。你可以根据需要加密它,但是它的用处是可疑的(它仍然会被交换到磁盘)。添加另一台机器进行加密存储也无济于事,因为受感染的机器只能让另一台机器进行解密。

编辑:想到这个:

  1. 生成随机的128位密钥。将其保存在会话中。
  2. 使用密钥加密数据。将其发送到&lt; input type =“hidden”&gt;
  3. 中的客户端
  4. 确认后,解密数据并提交交易。
  5. 攻击者需要妥协客户端和服务器以获取信用卡号(这样的攻击者可能已经拥有该号码)。在线服务器泄密仍将获得未来交易的信用卡号,但您无法真正阻止它。

    编辑:我忘记了细节。对于所有这些方案(不仅仅是我的),你还需要一个MAC来防止重放攻击(或者Eve会分散Alice的注意力,修改购物篮和账单地址,然后点击“确认”页面......)。一般情况下,您希望拥有所有交易数据的MAC(CC,CVV,交易ID,交易金额,账单地址......)。

答案 4 :(得分:4)

你是对的,使用会话对于存储敏感数据非常不安全,有很多方法可以通过以下方式进入会话:

<强> Session hijacking
Session fixation

我想到的最安全的方法是将信息存储在数据库中(暂时),然后在您需要的页面上读取该值。完成所有操作后,您可以将其删除。

请注意:

  • 在保存到数据库之前,您必须加密
  • 如果您使用共享主机
  • ,请务必小心
  • 确保在完成后删除

您可能会发现 this 反射性有用:)

答案 5 :(得分:3)

看来无论如何你触摸它,你需要提供安全的信用卡号码存储功能。如果服务器受到危害,它将在任何时候包含足够的信息来解密当前存储的信用卡号码(即密钥和加密号码)。可能的解决方案是使用充当“加密器/解密器”服务的内部服务器而不是其他任何东西。这种方式危及一台机器不会暴露信用卡号码。

答案 6 :(得分:3)

还有另外一种方法,但它需要Ajax。不存储信用卡号和评论页。

第1页:用于获取运费,账单和信用卡信息的表格。确保页面的“正文”(包括表单)位于具有唯一ID的DIV中,以允许您使用JavaScript引用它。

第2页:服务器上的一个文件,它接受带有表单字段的GET / POST请求,并根据您的喜好返回格式正确的“评论”页面。

结帐流程:

  1. 验证表格。
  2. 将信用卡相关字段复制到全局JavaScript变量中。
  3. 遍历表单字段并使用表单字段(不包括信用卡相关字段)构建查询/数据字符串
  4. 向“审核”页面发出Ajax请求,并使用表单字段/值传递查询字符串。在服务器上渲染并返回调用Ajax函数。
  5. 从Ajax请求中返回呈现的HTML审阅页面,并用它替换“DIV”容器中的内容(使用审阅HTML有效地替换表单和其他元素)。
  6. 使用JavaScript将存储在全局JS变量中的信用卡数据复制到审阅页面上的适当位置。您还可以将卡片数据复制到隐藏的表单字段,以便在用户从“审核”页面“完成”订单时提交。
  7. 用户从评论页面向服务器提交订单,使用处理器的网关执行卡验证,然后下订单或返回错误处理页面,从未存储卡详细信息。
  8. 我建议“下订单”功能执行完整的HTTP请求(而不是Ajax),以便使用不再存储全局JS变量中的卡数据的页面重新加载浏览器。
  9. 这有点像黑客攻击,但如果操作得当,它可以100%无缝地与用户保持一致,并且可以单次传输卡数据,而无需承担临时数据库存储等风险。

答案 7 :(得分:2)

这就是数据库的用途。我不确定这里的法律后果(根据国家和地区而有所不同),但一种方法是加密CC号码,并在用户收到后立即将其存储在数据库中。您可能希望将最后4位数字存储在单独的字段中,以便在需要时将其显示给用户。当您需要与服务器上的卡处理器进行交互时,请从数据库中检索并解密卡号。

答案 8 :(得分:2)

以下是我计划如何做的事情 - 当然是使用ssl的所有内容。

步骤1.用户输入CC信息并按下一个按钮。 CC信息会立即保存到CC处理器的数据库中,而不是您自己的数据库中,或者您将破坏PCI合规性。此步骤实际上并未向CC收取费用。但是你应该从处理器中收到一些识别CC信息的唯一ID。 (如果需要,可以在数据库中存储唯一ID)

步骤2.在确认页面上,使用他们为您提供的唯一ID从CC处理器检索CC信息。我的处理器只允许我检索CC的最后4个数字。

步骤3.确认购买并按下立即购买按钮后,使用唯一ID为信用卡充值。

步骤4.重定向到包含发票/收据的感谢页面,该发票/收据仅包含CC的最后4位数字(当然,您不必显示CC的最后4位,但我认为这是一个不错的选择要展示的东西)。

答案 9 :(得分:1)

您可以在会话中存储卡nr的哈希值,并在数据库中存储相同的哈希值和实际数量以及用户的会话ID。然后,对于每个页面,您可以检查哈希和会话信息以获取卡nr。

答案 10 :(得分:1)

稍后在付款处理(第3步的最后一部分)的某个时刻,您需要加密CC#(和CVC)以便能够将其发送到付款处理程序(我假设)

为什么不在收到确认页面所需的模糊处理旁边的信息时立即进行加密。 (这是第1步的最后一部分)

从现在开始,仅使用此加密或混淆数据,使CC公司成为唯一可以实际解密完整数据的公司。

答案 11 :(得分:1)

不需要会话或数据库来保存信息。

每个页面都是一个发布数据的表单。在每个后续页面上,上一页中的后期变量将添加到隐藏的表单字段中,以便下一个表单提交再次发布数据。这样就不会存储任何内容,但信息会在页面之间传递。这也迫使用户从头到尾完成整个过程,而不试图跳过步骤。

只要表单是通过HTTPS提交的,数据就会自动加密,并且安全负担在您的SSL证书提供商处。

许多热门商业网站实现了这一点。例如OSCommerce。

答案 12 :(得分:0)

使用标记化。它完全符合PCI DSS标准。

这里可以找到更多, https://www.pcisecuritystandards.org/documents/Tokenization_Guidelines_Info_Supplement.pdf