PHP:请求一种安全的方法从 $_POST 读取数据并写入文件

问题描述 投票:0回答:1

我要求一种安全的方法来从 $_POST 读取数据并写入 PHP 文件,因为我们的 PHP 代码被指出是不安全的。批评指出,我们的方式可能会给攻击者带来“破坏数据文件,或创建意想不到的新文件”的机会。

请考虑以下 PHP 代码:

function read_POST($key)
{
    $value = 'NULL';
    // Check whether the $_POST[$key] is set.
    if (array_key_exists($key, $_POST) !== true)
        return $value;
    // Restrict the length of user input.
    $value = substr($_POST[$key], 0, 128);
    // Remove possible scripting.
    $value = trim(preg_replace("/<\?.*\?>/", '', $value));
    // Convert all HTML special characters properly.
    $value = filter_var($value, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
    return $value;
}

$val1 = read_POST('user_inp1');
$val2 = read_POST('user_inp2');
$val3 = read_POST('user_inp3');
if (($f = fopen('/some/path/of/data/file', 'w')) == True) {
    fwrite($f, "Name: $val1\n");
    fwrite($f, "Location: $val2\n");
    fwrite($f, "Weather: $val3\n");
    fclose($f);
}

这里,为了简单起见,假设所有读取的值可以是任意(但安全清理过的)字符串。这段代码有安全问题吗?如果是,如何改进?

php security output user-input
1个回答
0
投票

攻击者无法任意销毁或创建文件,因为您只是写入一个特定文件,并且文件名是硬编码的。

这里有一些见解:


// Restrict the length of user input.
$value = substr($_POST[$key], 0, 128);

我假设您想支持 unicode 文本,毕竟我们在撰写本文时已经是 2022 年了。最简单的方法是设置一切(从您正在服务的网页开始,使用发送服务器用户数据的表单)将文本编码为 UTF-8。如果您不熟悉这个主题,您可以在网上找到大量资源。

表示以 UTF-8 感知方式修剪文本更安全:

$value = mb_substr( $value, 0, 128 );

请注意,这样您可能会以超过 128 字节大小的字符串结尾(因为 utf-8 编码字符最多可能占用 4 字节)。


// Remove possible scripting.
$value = trim(preg_replace("/<\?.*\?>/", '', $value));
// Convert all HTML special characters properly.
$value = filter_var($value, FILTER_SANITIZE_FULL_SPECIAL_CHARS);

我不会在这里这样做。您正在存储文本。 当您需要该文本时,您将根据使用它的位置对其进行转义。是网页吗?然后进行html编码。它是一个 url 片段吗?对其进行 url 编码。它是一个数据库吗?如果数据库配置为接收 UTF-8 文本,则无需执行任何操作。

您唯一需要做的就是避免换行符

\n
,因为您保存数据的方式。 如果您按照代码进行清理,那么换行符已经被删除。 如果您遵循我的建议,那么您就需要这样做。

$value = str_replace( "\n", " ", $value ); // let's turn them into spaces
$value = str_replace( "\r", " ", $value ); // let's do it for carriage returns too

您想要这样做,因为“伪造”的 POST 请求(例如,

user_inp3
sunny\nName: foo\nLocation: nowhere\nWeather: rainy
)会导致在输出文件上创建不需要的额外记录。


if (($f = fopen('/some/path/of/data/file', 'w')) == True) {
    fwrite($f, "Name: $val1\n");
    fwrite($f, "Location: $val2\n");
    fwrite($f, "Weather: $val3\n");
    fclose($f);
}

这里有两个问题:

首先如评论中所述,使用

w
选项和
fopen
将导致文件在每次脚本执行时被重写。 假设您需要保留数据使用
a
,新数据将附加到文件中。

其次,您可能会遇到并发问题:如果同时发生两个请求,您可能有两个 php 解释器实例同时写入输出文件,从而弄乱数据。

这就是为什么通常使用数据库来存储数据。

如果您需要坚持使用文件系统方法,则在写入之前锁定文件,确保锁定成功,然后在最后解锁。参见羊群

© www.soinside.com 2019 - 2024. All rights reserved.