【问题标题】:More tidy way of sanitizing input?更整洁的输入消毒方式?
【发布时间】:2013-03-21 22:43:01
【问题描述】:

我正在执行相当广泛的数据库插入前过滤器列表,我对代码的长度和丑陋感到非常沮丧:

/*******************************************************************
* START OF sanitising input 
********************************************************************/
// main user inputs
$title  = filter_var($place_ad['title'], FILTER_SANITIZE_STRING); 
$desc   = filter_var($place_ad['desc'], FILTER_SANITIZE_SPECIAL_CHARS);
$cat_1  = filter_var($place_ad['cat_1'], FILTER_SANITIZE_NUMBER_INT);
$cat_2  = filter_var($place_ad['cat_2'], FILTER_SANITIZE_NUMBER_INT);
$cat_3  = filter_var($place_ad['cat_3'], FILTER_SANITIZE_NUMBER_INT);
$price  = filter_var($place_ad['price'], FILTER_SANITIZE_NUMBER_FLOAT,FILTER_FLAG_ALLOW_FRACTION);
$suffix = filter_var($place_ad['suffix'], FILTER_SANITIZE_STRING); 

// check input
if(empty($title) || strlen($title) < 3 || strlen($title) > 100) { $error[] = 'Title field empty, too long or too short.'; }
if(empty($desc) || strlen($desc) < 3 || strlen($place_ad['desc']) > 5000) { $error[] = 'Description field empty, too long or too short.'; } 
if(empty($cat_1) || empty($cat_2)) { $error[] = 'You did not select a category for your listing.'; }
if(empty($price) || $price < 0 || $price > 1000000) { $error[] = 'Price field empty, too low or too high.'; }


// google location stuff
$lat    = filter_var($place_ad['lat'], FILTER_SANITIZE_NUMBER_FLOAT,FILTER_FLAG_ALLOW_FRACTION);    
$lng    = filter_var($place_ad['lng'], FILTER_SANITIZE_NUMBER_FLOAT,FILTER_FLAG_ALLOW_FRACTION);
$formatted_address   = filter_var($place_ad['formatted_address'], FILTER_SANITIZE_STRING);

// check input
if(empty($lat) || empty($lng)) { $error[] = 'Location error. No co-ordinates for your location.'; }


// account type
$registered = filter_var($place_ad['registered'], FILTER_SANITIZE_NUMBER_INT);


// money making extras
$extras = filter_var($place_ad['extras'], FILTER_SANITIZE_NUMBER_INT); //url encoded string
$icons  = filter_var($place_ad['icons'], FILTER_SANITIZE_STRING); //url encoded string
$premium= filter_var($place_ad['premium'], FILTER_SANITIZE_NUMBER_INT); //numeric float;
$bump   = filter_var($place_ad['bump'], FILTER_SANITIZE_NUMBER_INT); //numeric float;


// user details field
if ($registered == '1') // Registering as new user
{

    $type   = filter_var($place_ad['n_type'], FILTER_SANITIZE_NUMBER_INT);
    $name   = filter_var($place_ad['n_name'], FILTER_SANITIZE_STRING);
    $phone  = filter_var($place_ad['n_phone'], FILTER_SANITIZE_STRING);
    $email  = filter_var($place_ad['n_email'], FILTER_SANITIZE_EMAIL);
    $pass   = filter_var($place_ad['n_password'], FILTER_UNSAFE_RAW);

    if(empty($type)) { $error[] = 'Type field error.'; }
    if(empty($name) || strlen($name) > 100) { $error[] = 'You did not enter your name or name too long.'; }
    if(empty($email) || strlen($email) < 5 || strlen($email) > 100) { $error[] = 'You did not enter a valid email.'; }
    if(!filter_var($email, FILTER_VALIDATE_EMAIL)) { $error[] = 'You did not enter a valid email.'; }
    if(empty($pass) || strlen($pass) < 6 || strlen($pass) > 100) { $error[] = 'Your password must be at least 6 characters.'; }

}
elseif ($registered =='2') // registered user
{
    $email  = filter_input($place_ad['n_email'], FILTER_SANITIZE_EMAIL);
    $pass   = filter_input($place_ad['n_password'], FILTER_UNSAFE_RAW);

    if(empty($email) || strlen($email) < 5 || strlen($email) > 100) { $error[] = 'You did not enter a valid email.'; }
    if(empty($pass) || strlen($pass) < 6 || strlen($pass) > 100) { $error[] = 'Your password must be at least 6 characters.'; }
}
elseif ($registered == '3') // dont wanna register details
{
    $name   = filter_input($place_ad['n_name'], FILTER_SANITIZE_STRING);
    $phone  = filter_input($place_ad['n_phone'], FILTER_SANITIZE_STRING);
    $email  = filter_input($place_ad['n_email'], FILTER_SANITIZE_EMAIL);

    if(empty($name) || strlen($name) > 100) { $error[] = 'You did not enter your name or name too long.'; }
    if(empty($email) || strlen($email) < 5 || strlen($email) > 100) { $error[] = 'You did not enter a valid email.'; }

}
/*******************************************************************
* END OF Sanitising input
********************************************************************/

我认为我的很多代码都是“不必要的”,但我认为如果我要删除它可能是不好的编码习惯。

例如,我可以放弃所有 FILTER_SANITIZE_NUMBER* 过滤器,因为数据库已正确设置有 INT/FLOAT 字段。

我还可以放弃很多“大于 >”检查,因为其中大多数只是为了防止用户输入大量数据(这将再次受到数据库字段长度的限制)。

其他人都有这么丑陋的用户输入验证代码吗?

------------------编辑----------------------

非常感谢您提供的信息。由于我正在使用 PDO,我想我可能会尝试将其压缩更多,但我可以问以下问题:

  1. 对于用户不容易破坏输入的单选按钮和选择框等输入字段,您认为仅绑定 PDO 常量就足够了吗?这些值与数据库中的 enum 和 tinyint(1) 字段相关联,并且在表单规范之外操作这些值不会让用户实现任何目标。
  2. 我还使用 filter_var 使用户输入适合在 UTF8 编码页面上显示。我相信这实际上只将 和几个其他字符编码到它们的实体中。只使用 htmlentities 会更好吗?

【问题讨论】:

  • 你在那里做两件事,净化和价值约束。复杂性严格来自不使用实用程序代码。许多表单处理程序或验证库允许为此指定规则集,例如array("title"=&gt;["string"], "cat_1"=&gt;["int", "&lt;100"]),减少开销。事实上,数据库也可以应用这种验证。准备好的语句使filter_var 调用经常变得多余。我个人使用$_POST-&gt;int["cat_1"]$_GET-&gt;text-&gt;ascii["title"]。基本上,只需使用库来增强可读性或自动执行此类任务。
  • 你的方法真的很有趣。这只是使用 OOP 而不是 $value = (int)$value 的内置方式转换值,还是您正在使用的自定义库?

标签: php validation filter-var


【解决方案1】:

IMO,尽管看起来很笨拙,但在应用层如果您想要通知用户并让他们更正输入的数据,所有验证的明显过度是必要的。否则,您可以让数据访问层(例如 PDO)在为查询绑定值时通过分配 PDO constants 来清理数据。

您可以根据需要使清理过程变得复杂或简单,您只需要确定哪种配置最适合您的需求。

---- 更新 -----

一些建议可以使代码不那么不堪重负。这假设您无法加载像 Zend 或 Symfony Validator Component 这样的供应商库。

class Validator
{
    public function validateString($string, array $options)
    {
        $min = ((isset($options['min'])) && (strlen($options['min'] > 1))) ? $options['min'] : null;
        $max = ((isset($options['max'])) && (strlen($options['max'] > 1))) ? $options['max'] : null;

        $string = filter_var($string, FILTER_SANITIZE_STRING);

        if (empty($string)) {
            throw new Exception(sprintf('Empty value: %s', $string));
        }

        if ((false === is_null($min) && (strlen($string) < $min)) {
            throw new Exception(sprintf('Value too short: %s', $string));
        }

        if ((false === is_null($max) && (strlen($string) > $max)) {
            throw new Exception(sprintf('Value too long: %s', $string));
        }
    }

    return $string;
}

// Calling code

try {
    $title = Validator::validateString($place_ad['title'], array('min' => 3, 'max' => 100));
} catch (Exception $e) {
    $errors[] = 'Title field empty, too long or too short.';
    // OR
    $errors[] = $e->getMessage();
}

try {
    $title = Validator::validateString($place_ad['desc'], array('min' => 3, 'max' => 5000));
} catch (Exception $e) {
    $errors[] = 'Description field empty, too long or too short.';
    // OR
    $errors[] = $e->getMessage();
}

希望你能得到图片。通过使方法对数据类型通用,并通过一系列选项灵活使用,您可以重用代码以减少占用空间,但仍保持您希望达到的数据清理水平。

【讨论】:

    【解决方案2】:

    假设信息将显示在网站上(作为 HTML 的一部分),您需要做两件事:

    1. 清理数据库查询(或使用准备好的语句)
    2. 为 HTML 显示清理。

    第一个很简单,如果你经常使用 mysqli 或 PDO,你应该已经知道怎么做。这是一个关于这个主题的问题:PHP PDO prepared statements

    在您的情况下,第二个更棘手。您需要遍历每个变量,并将其传递给清理函数。无论是htmlspecialchars 还是更激进的 HTML 过滤器,例如 HTML Purifier。这是一个关于这个主题的问题:How to strip specific tags and specific attributes from a string?

    如果数组中有值,可以使用array_walkarray_map 在几行内清理整个数组。

    【讨论】:

    • 这并不能真正回答问题。 OP 不仅仅是清理 DB 插入或 HTML,他还试图对输入进行实际的质量控制:确保某些输入是数字,某些是字符串,某些具有非空值,某些被格式化为电子邮件,等等……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 2020-12-29
    • 1970-01-01
    相关资源
    最近更新 更多