1

我有一个 PHP 应用程序,我希望某些对象以下列方式持久存在:

  1. 该对象不得存在于 $_SESSION 中。单独的 Web 浏览器窗口必须控制对象的单独实例。
  2. 最终用户不能通过手动更改 $_REQUEST 变量的内容来修改对象(如果发生这种情况,则应将请求视为已损坏)。

是否有最佳实践/正确的方法来做到这一点?随着 PHP 变得越来越面向对象,我担心我正在重新发明轮子。

这段代码的主要目的是允许在不使用数据库的情况下创建和操作复杂对象,直到它们被提交,然后我将使用适当的事务将它们完全提交到数据库。我想让我的数据库只包含完整的发票,或者根本不包含发票。

我目前的方法如下:

<?php

include('encrypt.php');
include('invoice.class.php');

if(isset($_REQUEST['invoice']))
{
    $invoice = unserialize(decrypt(base64_decode($_REQUEST['invoice'])));
    if(!($invoice instanceOf invoice)) throw new exception('Something bad happened');
}
else
{
    // Some pages throw an exception if the $_REQUEST doesn't exist.
    $invoice = new invoice();
}

if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'addLine')
{
    $invoice->addLine(new invoiceLine($_REQUEST['description'], $_REQUEST['qty'], $_REQUEST['unitprice']);
}

?>
<form action="index.php" method="post">
<input type="text" name="qty" />
...
<input type="hidden" name="invoice" value="<?php echo(base64_encode(encrypt(serialize($invoice)))); ?>" />
</form>
4

9 回答 9

4

这里有一个技巧:把它放在饼干里!

简洁的算法:

$data = 序列化($object); $时间=时间();$signature = sha1($serverSideSecret . $time . $data); $cookie = base64("$signature-$time-$data");

好处是你

a) 可以在需要时使 cookie 过期,因为您使用时间戳作为签名哈希的一部分。

b) 可以验证数据没有在客户端被修改,因为您可以从 cookie 中的数据段重新创建散列。

此外,如果它太大,您不必将整个对象存储在 cookie 中。只需将您需要的数据存储在服务器上,并使用 cookie 中的数据作为密钥。

我不能把算法归功于这个算法,我是从 Flickr 成名的 Cal Henderson 那里学到的。

编辑:如果您发现使用 cookie 过于复杂,请忘记它们,并将本应进入 cookie 的数据存储在隐藏的表单字段中。

于 2009-03-06T07:04:01.543 回答
3

您还可以使用简单的隐藏表单输入在客户端上保存状态,无需 cookie。只要数据(可能是序列化的 blob)经过加密和签名,用户就无法在不中断会话的情况下对其进行修改。

Steve Gibson 将这种方法用于他的定制电子商务系统。虽然他的代码不是开源的,但他在Security Now 第 109 集“GRC 的电子商务系统”中彻底解释了保存状态的方法,无需在服务器上存储敏感数据或需要 cookie 支持。

于 2009-03-06T07:28:40.750 回答
2

我要做的是将加密密钥(而不是整个结构,就像您的示例那样)存储在隐藏的表单变量中。此键是uncreated_invoices存储不完整发票的表的索引。

在页面之间,您更新 uncreated_invoices 表中的数据,完成后,将其拉出并提交。如果您将用户名放入 uncreate_invoices 表中,这也会让他们从上次中断的地方继续(不确定这是否是一个有效的用例)。无论如何,将用户名放在其中可能是个好主意,这样人们就不能试图劫持其他人的发票。

如果您愿意,您可能可以在 uncreated_invoices 表中存储序列化数据。就个人而言,我会对其进行规范化(多少取决于您的架构),以便您可以轻松添加/删除各个部分。

编辑:我忘了提到你应该uncreated_invoices定期清理桌子,这样它就不会被过期的发票填满。

于 2009-03-06T06:45:56.690 回答
1

您不能将数据存储在 $_SESSION 中的数组中,然后为每个窗口设置一个唯一的 ID。如果每个窗口都有唯一的 id,您可以将 id 作为表单的一部分传递。根据窗口 ID 存储/检索会话或数据库中的数据。

所以像这样的东西?$_SESSION['data'][$windowid]->$objectname

于 2009-03-06T08:22:22.730 回答
0

如果你不能使用 SESSION 那么你必须自己持久化数据。您必须将数据放置在可以持久保存的位置,例如数据库或其他文件。除了 SESSION 之外,它是保存数据的唯一方法。

我收回这一点,您可以在每个 HTML 页面中发送数据并在本地使用它。每个接受数据的页面都必须创建延续数据序列的 HTML/Javascript。

于 2009-03-06T06:16:05.433 回答
0

如果您将内容存储在客户端,则可以对其进行修改。是否有您不想将对象存储在会话中的特定原因?如果存储对象实际上不可行,则需要将其保存在服务器上的其他位置。

于 2009-03-06T06:17:00.457 回答
0

没关系,他们将能够添加他们想要的所有项目。您的代码中的问题是您从请求中获取项目描述符、数量和价格,价格确实应该在后台查找,否则,用户可以手工制作他们的价格。哎呀,负值物品,你可以免费得到东西。

于 2009-03-06T06:19:13.743 回答
0

你想存储一些东西。这通常在数据库中完成。如果不在数据库中查找,您如何知道某物的价格?所以使用相同的数据库来存储有关当前用户/会话的信息。

于 2009-03-06T06:22:00.850 回答
0

您可以使用魔术方法 __sleep()__wakeup()

于 2009-03-06T07:31:24.443 回答