在sql中引用标记导致问题

时间:2010-09-28 16:09:02

标签: php mysql sql-injection

我有一个简单的html输入文本框,形式非常简单。此表单的信息表单通过sql字符串传输到mysql数据库。

除非有人输入“或”,否则一切都很顺畅。我不想限制用户输入的内容。

在针对数据库运行查询之前,是否应该查找并替换字符串?

有简单的方法吗?

这里有一些代码:

<?php
session_start();
if (empty($_SESSION['user']) && empty($_REQUEST['form'])) //check this code!!1
{
    exit;
}
if (isset($_REQUEST['Submit']))
{
    //echo "Let's process this form!";
    include "config.php";
    include "mail.php";
    if ($_REQUEST['form'] == "profile")
    {//public profile
        //print_r($_REQUEST);
        //"UPDATE `tims`.`pending_profile` SET `nickname` = 'I Don''t Have One' WHERE `pending_profile`.`id` = 1;";
        $sql = "INSERT INTO `tims`.`pending_profile`"
                . "(`id`, `nickname`, `location`, `role`, `yog`, `interests`, `favMoment`, `gainThisYr`, `futurePlans`, `bio`) \n"
                . "VALUES ('" . $_SESSION['id'] . "', '" . $_REQUEST['nickname'] . "', '" . $_REQUEST['town'] . "', '" . $_REQUEST['role'] . "', '" . $_REQUEST['yog'] . "', '" . $_REQUEST['interests'] . "', '" . $_REQUEST['fav_moment'] . "', '" . $_REQUEST['gain'] . "', '" . $_REQUEST['future'] . "', '" . $_REQUEST['bio'] . "')\n"
                . "ON DUPLICATE KEY UPDATE nickname ='" . $_REQUEST['nickname'] . "', location='" . $_REQUEST['town'] . "', role= '" . $_REQUEST['role'] . "', yog='" . $_REQUEST['yog'] . "', interests='" . $_REQUEST['interests'] . "', favMoment='" . $_REQUEST['fav_moment'] . "', gainThisYr='" . $_REQUEST['gain'] . "', futurePlans='" .       $_REQUEST['future'] . "', bio='" . $_REQUEST['bio'] . "'\n";  
        $qry = mysql_query($sql) or die(mysql_error());

//@todo overlay this
//http://flowplayer.org/tools/overlay/index.html
        //send mail to moderators
        include "vars.php";
        $to = $captMail;
        $prof = implode("\n", $_REQUEST);
        $subject = "Moderation Needed";
        $body = $_SESSION['fullname'] . " Has just changed their public profile.\n" .
                "Please login here to moderate their changes:\n" .
                //"http://team2648.com/OPIS/login.php?page=manage".
                "http://www." . $sysurl . "/login.php?page=manage\n" .
                "Best,\n" .
                "Blake\n\n\n" .
                "Click here to accept the profile bleow\n\n" .
                "http://www." . $sysurl . "/login.php?page=manage&acceptID=".$_SESSION['id']."\n" .
                $prof;
        mailer($to, $subject, $body);
        $to = $mentorMail;
        mailer($to, $subject, $body);

        echo "<link href=\"../css/styling.css\" rel=\"stylesheet\" type=\"text/css\" media=\"screen\" />";
        echo "<div class =\"widget\" style=\"width:350px\">";
        echo "Your changes have been saved, they will not go live until reviewed by a moderator";
        echo "<br>";
        echo "<a href=\"../\">Click here to continue</a>";
        echo "</div>";
    }
    exit;
}
$sql = "SELECT * FROM `pending_profile` WHERE id ='" . $_SESSION['id'] . "'";
$qry = mysql_query($sql) or die(mysql_error());
$row = mysql_fetch_assoc($qry);
?>
<!--<h3>Use this page to manage your profile information</h3>-->
<h4>Public Profile</h4>
<strong>NOTE:</strong> Fields filled with [NONE] will not show on the website.
<br />
<form id="profile" name="profile" method="get" action="lib/preview.php">
    <input type="hidden" value="profile" name="form">
    <input type="hidden" value="<?php echo $_SESSION['id']; ?>" name="id">
    <table>
        <tr>
            <td><label for="myname">Hello, My name is:</label><td>
            <td><input type="text" readonly="readonly" name="myname" value="<?php echo $_SESSION['firstname']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="nickname">But I like to be called:</label><td>
            <td><input type="text" name="nickname" value="<?php echo $row['nickname']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="town">I live in:</label><td>
            <td><input type="text" name="town" value="<?php echo $row['location']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="role">My role on the team is:</label><td>
            <td><input type="text" name="role" value="<?php echo $row['role']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="yog">I will graduate High School in:</label><td>
            <td><input type="text" name="yog" value="<?php echo $row['yog']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="interests">Some of my interests are:</label><td>
            <td><input type="text" name="interests" value="<?php echo $row['interests']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="fav_moment">One of my favorite team moments:</label><td>
            <td><input type="text" name="fav_moment" value="<?php echo $row['favMoment']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="gain">I would like to gain the following this year:</label><td>
            <td><input type="text" name="gain" value="<?php echo $row['gainThisYr']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="future">My future plans include:</label><td>
            <td><input type="text" name="future" value="<?php echo $row['futurePlans']; ?>"/><td>
        </tr>
        <tr>
            <td><label for="bio">My Bio:</label><td>
            <td><textarea name="bio" ><?php echo $row['bio']; ?></textarea><td>
        </tr>
    </table>
    * All fields are required.
<?php
include "disclaimer.php";
// @todo add js validation of all fields filled in
?>
    <br><input type="submit" name="Submit" value=" I Agree, Preview "/>

</form>

10 个答案:

答案 0 :(得分:11)

您的代码中填充了sql injection opportunities。使用PDO查看prepared statements。或至少mysql_real_escape_string。不要使用你的代码。

也...

不要使用$ _REQUEST。使用适当的全局$ _POST或$ _GET。 GET请求不应该更改任何内容,因此您应该使用$ _POST。

答案 1 :(得分:5)

在将用户输入的所有输入数据库之前,必须使用mysql_real_escape_string功能。

例如:

$_REQUEST['nickname']更改为mysql_real_escape_string($_REQUEST['nickname'])

或者使用prepared statements

答案 2 :(得分:5)

访问数据库时始终使用参数化查询。正如您所做的那样构建动态SQL语句是对SQL注入攻击的邀请(以及您所看到的错误。)

在PHP中,执行此操作的代码类似于

$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');
$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
$stmt->bind_param('sssd', $code, $language, $official, $percent);

(无耻地从PHP手册中获取)。

答案 3 :(得分:4)

正如其他人所提到的,你需要处理字符串中的字面引号字符,否则你很容易受到SQL injection攻击。

有关更多示例和解决方案,请参阅我的演示文稿SQL Injection Myths and Fallacies

使用查询参数是一种有效的防御,它实际上更容易编写并使您的代码看起来更好。我建议使用PDO,因为它允许您使用命名参数。

还考虑使用PHP的heredoc syntax来简化编写SQL代码,而不必担心所有的双引号和串联字符串。

$sql = <<<END_SQL
INSERT INTO `tims`.`pending_profile`
(`id`, `nickname`, `location`, `role`, `yog`, `interests`, `favMoment`,
 `gainThisYr`, `futurePlans`, `bio`)
VALUES (:id, :nickname, :town, :role:, :yog, :interests, :fav_moment, 
 :gain, :future, :bio)
ON DUPLICATE KEY UPDATE
  nickname    = :nickname,
  location    = :town,
  role        = :role,
  yog         = :yog,
  interests   = :interests,
  favMoment   = :fav_moment,
  gainThisYr  = :gain,
  futurePlans = :future,
  bio         = :bio
END_SQL;

$stmt = $pdo->prepare($sql);
$success = $stmt->execute(array(
    ':id'         => (int) $_SESSION['id'],
    ':nickname'   => $_POST['nickname'],
    ':town'       => $_POST['town'],
    ':role'       => $_POST['role'],
    ':yog'        => $_POST['yog'],
    ':interests'  => $_POST['interests'],
    ':fav_moment' => $_POST['fav_moment'],
    ':gain'       => $_POST['gain'],
    ':future'     => $_POST['future'],
    ':bio'        => $_POST['bio'],
));

注意:我将$ _REQUEST更改为$ _POST,因为您永远不应该使用修改数据的GET请求。搜索引擎会跟踪链接,因此您可能会发现Google抓取您的网站并无意中修改了您的数据库。当操作要存储或修改某些内容时,始终使用POST请求。

对于简单的情况,你甚至不必使用参数,如果你可以将你的变量强制转换成可以安全使用的东西,比如一个普通的整数:

$id = (int) $_SESSION['id'];
$sql = "SELECT * FROM `pending_profile` WHERE id = {$id}";
$stmt = $pdo->query($sql);
$row = $stmt->fetch();

除了SQL注入风险之外,您的代码还充满了不同类型的安全风险,称为Cross-Site Scripting或XSS。当您将用户提供的内容回显到HTML输出时,会发生这种情况,而不会转义HTML中特殊的字符,例如<&。攻击者可以输入他们的名字,包括javascript代码,从而导致恶作剧。

XSS的修复基本上是对你回应的任何内容使用htmlentities(),如果它可能来自用户输入或任何其他不受信任的来源(如从数据库或文件或Web服务中读取)

<td><input type="text" name="nickname" value="<?php echo htmlentities($row['nickname']); ?>"/><td>

如果您为网络编写代码,您确实需要研究这些安全漏洞。它们是互联网上很多麻烦的来源。请参阅OWASPSANS Top 25 Most Dangerous Software Errors

答案 4 :(得分:3)

答案 5 :(得分:3)

在将表单中的变量插入数据库之前,您不会因为没有清理表单中的变量而受到打击。

PHP手册中有过滤/消毒的示例....

http://php.net/manual/en/filter.filters.sanitize.php

自从使用php和mysql以来,另一种流行的方法是这样的......

$var = mysql_real_escape_string($_POST['var']);

更多信息:

http://php.net/manual/en/function.mysql-real-escape-string.php

希望有所帮助。

答案 6 :(得分:2)

如果(int)变量必须是数字,您也可以使用$_POST

答案 7 :(得分:1)

使用带引号的值的每个位置都使用addslashes($mystringwithquotes)方法在引号前添加斜杠。

请确保不要将它放在完整的SQL字符串周围,因为您仍然希望保留所需的字符串。

例如在您的代码中:

    $sql = "INSERT INTO `tims`.`pending_profile`" 
                . "(`id`, `nickname`, `location`, `role`, `yog`, `interests`, `favMoment`, `gainThisYr`, `futurePlans`, `bio`) \n" 
                . "VALUES ('" . $_SESSION['id'] . "', '" . $_REQUEST['nickname'] . "', '" . $_REQUEST['town'] . "', '" . $_REQUEST['role'] . "', '" . $_REQUEST['yog'] . "', '" . $_REQUEST['interests'] . "', '" . $_REQUEST['fav_moment'] . "', '" . $_REQUEST['gain'] . "', '" . $_REQUEST['future'] . "', '" . $_REQUEST['bio'] . "')\n" 
                . "ON DUPLICATE KEY UPDATE nickname ='" . $_REQUEST['nickname'] . "', location='" . $_REQUEST['town'] . "', role= '" . $_REQUEST['role'] . "', yog='" . $_REQUEST['yog'] . "', interests='" . $_REQUEST['interests'] . "', favMoment='" . $_REQUEST['fav_moment'] . "', gainThisYr='" . $_REQUEST['gain'] . "', futurePlans='" .       $_REQUEST['future'] . "', bio='" . $_REQUEST['bio'] . "'\n";   
        $qry = mysql_query($sql) or die(mysql_error()); 

您希望将其置于addslashes($_SESSION['id'])addslashes($_SESSION['nickname'])等值,但不是

   $sql = "INSERT INTO `tims`.`pending_profile`" 

答案 8 :(得分:0)

使用传统的“普通”(非参数化)查询,您必须遵循3条规则

  • 必须转义所有字符串(引用的数据部分)
  • 所有数字必须明确地转换为其类型
  • 所有标识符或运算符都应从先前定义的变体中选择。

你也可以使用一些帮助函数自动组装你的查询。

function dbSet($fields,&$source) {
  $set='';
  foreach ($fields as $field) {
    if (isset($source[$field])) {
      $set.="`$field`='".mysql_real_escape_string($source[$field])."', ";
    }
  }
  return substr($set, 0, -2); 
}

但你的代码太脏了,无法用它做出一个有效的例子 这是虚拟的

$fields = explode(" ","name surname lastname address zip fax phone");
$query  = "INSERT INTO $table SET ".dbSet($fields, $_POST);

答案 9 :(得分:0)

对于未来的人来说,这对我有用

string addslashes ( string $str )

  

(PHP 4,PHP 5,PHP 7)   addslashes - 使用斜杠引用字符串

     

在需要转义的字符之前返回带反斜杠的字符串。这些字符是单引号('),双引号(“),反斜杠()和NUL(NULL字节)。   Read more

<?php
$str = "Is your name O'Reilly?";

// Outputs: Is your name O\'Reilly?
echo addslashes($str);
?>