PHP 表单和 DB 查询输入验证

标签 php mysql forms validation

(将其重新命名为“测试 GET 请求中的非空查询?”,因为当我写完它时,这实际上并不是我问题的重要部分。)

我正在帮助一位系谱学家 friend 编写一个简单的数据库搜索脚本。如果用户将除一个输入字段以外的所有输入字段留空也没关系。我想区分三种情况:

    1. 有人浏览不带查询参数的网址
  • 提交表单,所有字段均为空
  • 提交表单时至少有一个字段非空

我可能可以以同样的方式对待 1 和 2;这不需要很花哨。

该代码之前有一个 if (isset($_POST['submit'])),并且显然曾经可以工作。我想使用 GET 查询,以便人们可以为搜索添加书签。我首先假设我可以只测试 $_GET['submit'],但它从未设置。对于 GET 查询,我假设表单中需要一个名为 submit 的隐藏文本字段,以在查询 URL 中生成 ?submit= ?让 URL 进一步困惑不是我想要的,所以我想我会放弃这个想法。

Google 在论坛等上发现了很多 isset($_GET['submit']) 的点击率。通常问题中的其他错误代码会引起人们的注意,因此我从未找到任何关于实际使用该构造与 GET 查询的讨论。因此,作为主要问题的旁白,我很好奇 isset($_GET['submit']) 是如何变得半普遍的。这通常只是像我这样天真的人将 POST 更改为 GET 吗?

由于我是 PHP 新手,如果有人能指出搜索脚本这段代码中的任何其他问题,我将不胜感激。我很确定它对 SQL 注入(inject)是开放的,所以我需要获取转义函数的输出来清理它们。检查输入字符串的长度也很常见吗?向此脚本提交查询的表单有长度限制。

(我知道 SO 不欢迎代码审查问题,所以请随意忽略我猜的问题的这一部分。)

msearchresultlist.php:
从表单(包含在此代码块的末尾)接收诸如 ...php/?Surname=Cordes&MaidenName=&GivenName=&Obit= 之类的查询。 它打印出匹配表。
每行中的一个条目是指向该讣告详细信息页面的超链接。

<?PHP
... check that user is logged in to members area ...

if (some_condition_discussed_above) {
    ... open the DB, with hard-coded username/password :( ...

    $GivenName = $_GET['GivenName'];
    if ($GivenName == "")
        {$GivenName = '%';}

    $Surname = $_GET['Surname'];
    if ($Surname == "")
        {$Surname = '%';}

    $MaidenName = $_GET['MaidenName'];
    if ($MaidenName == "")
        {$MaidenName = '%';}

    $Obit = $_GET['Obit'];

    $result = mysql_query ("SELECT * FROM obits
    WHERE GivenName LIKE '%$GivenName%'
    AND MaidenName LIKE '$MaidenName%'
    AND Surname LIKE '$Surname%'
    AND Obit LIKE '%$Obit%'
    ORDER BY Surname ASC, GivenName ASC");
} else {
    $noquery = true;
}
?>


<!DOCTYPE html>
<html lang="en">
... some stuff on the page


<?php

if ($noquery) {
    print "<p>No query found.</p>";
} else {

    print "<table width='600' cellpadding='10px'>";

    if($row = mysql_fetch_array($result)) {
    do {

    print "<tr>";
        print "<td>".$row['Surname']."</td>";
        print "<td>".$row['MaidenName']."</td>";
            print "<td>" . '<a href="msearchresultform.php?ID='.$row['ID'].'">'.$row['GivenName'].'</a>'. "</td>";

        print "<td>".$row['DOD']."</td>";
        print"</tr>";
    } while($row = mysql_fetch_array($result));


---- the form, on another page:
<form action=msearchresultlist.php method=GET>

When searching for a name with an apostrophe, such as O'Neil, use a double apostrophe, ie. O''Neil, not a quote but a double apostrophe. </br>
</br>

Search for:
<p>Last Name: <input type=text name=Surname size=15 maxlength=15>
<p>Maiden Name: <input type=text name=MaidenName size=15 maxlength=15>
<p>Given Name: <input type=text name=GivenName size=15 maxlength=15>
<p>Use % in front of each word in the Obit field, ie %Bridgewater% %Joudrey%
<p>Obit: <input type=text name=Obit size=50 maxlength=50>
<p>
<input type=submit>

我知道搜索可以通过搜索单个通配符或其他内容来转储整个数据库。我认为这是我 friend 想要的结果,但我不希望它意外发生。即,仅当用户明确搜索通配符时,而不仅仅是提交空表单。此高级搜索脚本仅适用于经过身份验证的用户。有一个公开的仅姓氏版本(位于 nsobits.ca )

无论如何,如果我不清楚我要问什么:我可以做一些聪明的事情来检查表格中是否填写了某些内容吗?就像某种检查 $_GET 至少其中一个键是否具有非零长度值的语法一样?或者我应该这样做if ($GivenName || $Surname || ...)

最佳答案

我想提的第一件事是使用旧的、已弃用的 mysql*()_ 函数。您确实应该使用 PDO 或 MySQLi 库。 有一个really good reply关于如何防止 SQL 注入(inject),以及如何正确使用 PDO/MySQLi。我强烈推荐阅读。

至于代码,我大约是这样做的:(我的注释带有前缀CF)

// ... open the DB, with hard-coded username/password :( ...

/**
 * Parses the input, if any, and returns an iterative with the results.
 * @param PDO $db
 * @return boolean|multitype:
 */
function parse_submit($db)
{
    // ... check that user is logged in to members area ...
    if (! isset($_GET['submit_btn'])) {
        // CF: False == No form submitted. We're testing for this specifically later.
        return false;
    }

    // CF: Used for taking care of input, and checking whether we have input.
    $params = array();
    $submitted = false;

    // CF: Preferably add some proper input validation here, to make sure you get what could be a name.
    if (empty($_GET['GivenName'] == "")) {
        $params[':given'] = '%';
    } else {
        $params[':given'] = '%' . $_GET['GivenName'] . '%';
        $submitted = true;  // Update: Was missing this line in my original posting.
    }

    /*
     * CF: Do the rest of the params as above.
     */

    // CF: Here we test for empty submission.
    if (! $submitted) {
        // Return empty array to simulate a non-hit query.
        return array();
    }

    $stmt = $db->prepare("SELECT * FROM obits
        WHERE GivenName LIKE :given
        AND MaidenName LIKE :maiden
        AND Surname LIKE :surname
        AND Obit LIKE :obit
        ORDER BY Surname ASC, GivenName ASC");
    return $stmt->exec($params);
}

/**
 * Shortcut version of htmlspecialchars ()
 * 
 * @param string $string
 * @return string
 */
function hs($string)
{
    return htmlspecialchars($string, null, 'utf-8');
}

$persons = parse_submit($db);

?>
<!DOCTYPE html>
<html lang="en">
... some stuff on the page

<?php

if ($persons === false) {
    print "<p>No query found</p>";
} elseif (empty($persons)) {
    print "<p>No search results found.</p>";
} else {
    // CF: Using a output variable and a template to keep things clean.
    $output = "<table>";
    $outTemplate = <<<EOHTML
    <tr>
        <td>%1\$s</td>
        <td>%2\$s</td>
        <td><a href="msearchresultform.php?ID=%3\$d">%4\$s</a></td>
        <td>%5\$s</td>
    </tr>

EOHTML;

    // CF: Since PDO result sets are iterative using PHP's functions, we use it directly here.
    foreach ($persons as $r) {
        $output .= sprintf($outTemplate, hs($r['Surname']), hs($r['MaidenName']), $r['ID'], hs($r['GivenName']), hs($r['DOD']));
    }

    echo $output . "</table>";
}

为此,您需要将 form 标记的 action 属性更改为 get。反射(reflect)将数据发送到服务器的方法的变化,以及您尝试使用的数组名称的变化。 我还建议将提交按钮的名称更改为“提交”以外的名称,以避免 DOM 事件出现问题。

您要问的“聪明”的事情是我的示例中的 $subscribed 变量。 ;)

我目前需要改进的最后一个领域是编码风格的使用。在您的示例中,没有应用明确的样式,花括号或多或少是随机放置的。坚持哪种编码风格并不重要,重要的是选择一种并严格遵守。这样做可以为你自己和他人在未来省去很多麻烦。 recommended style at this point in time is PSR-2 ,这是大多数主要 PHP 库正在使用的。

当您对 PHP 有了更多的经验时,您可能还想考虑使用模板引擎和框架。只要您掌握了所有基础知识,它们就会极大地帮助您提高工作效率。

帖子编辑更新: 我发现您根本没有命名输入控件,这就是为什么您在 URL 中看不到“submit=”键值参数。

PS:具有安全意识的金星!

关于PHP 表单和 DB 查询输入验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31255713/

相关文章:

php - Laravel 4 - 从数据库中检索对象

mysql - 复杂 ID 的 SQL 查询

mysql - 如何对子表求和?

html - 如何在没有 JS 的情况下从链接进行 HTTP POST

php - 使用 javascript 到 php 的表单验证

php - mysql 更新查询将多个条目插入单列时出错

javascript - 发送的值被视为引用而不是值 javascript

PHP do/while 准备语句失败

php - Wordpress - 用户个人资料编辑的表单验证

sql - MySQL严格选择涉及多对多表的行