php防注入函数 - 如何在PHP中阻止SQL注入?




php防止sql注入 (19)

安全警告 :此答案与安全最佳实践不符。 转义不足以防止SQL注入 ,而是使用预处理语句 。 使用下面列出的策略需要您自担风险。 (另外,在PHP 7中删除了mysql_real_escape_string() 。)

你可以做一些基本的事情:

$safe_variable = mysql_real_escape_string($_POST["user-input"]);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

这不会解决所有问题,但它是一个非常好的垫脚石。 我省略了明显的项目,例如检查变量的存在,格式(数字,字母等)。

如果插入用户输入而不修改SQL查询,则应用程序容易受到SQL注入的攻击,如下例所示:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

那是因为用户可以输入类似value'); DROP TABLE table;-- value'); DROP TABLE table;-- ,查询变为:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

可以采取哪些措施来防止这种情况发生?


警告:这个答案的示例代码(如问题的示例代码)使用PHP的mysql扩展,在PHP 5.5.0中已弃用并在PHP 7.0.0中完全删除。

如果您使用的是最新版本的PHP,则下面列出的mysql_real_escape_string选项将不再可用(尽管mysqli::escape_string是现代版本的等价物)。 目前, mysql_real_escape_string选项仅对旧版PHP上的遗留代码有意义。

您有两个选项 - 转义unsafe_variable的特殊字符,或使用参数化查询。 两者都可以保护您免受SQL注入。 参数化查询被认为是更好的做法,但在使用之前需要在PHP中更改为更新的mysql扩展。

我们将首先覆盖较低影响的字符串。

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

另请参见mysql_real_escape_string函数的详细信息。

要使用参数化查询,您需要使用MySQLi而不是MySQL函数。 要重写您的示例,我们需要类似以下内容。

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

你想要阅读的关键功能是mysqli::prepare

此外,正如其他人所建议的那样,您可能会发现使用PDO类的步骤来提升抽象层是有用的/更容易的。

请注意,您询问的案例非常简单,更复杂的案例可能需要更复杂的方法。 特别是:

  • 如果要根据用户输入更改SQL的结构,参数化查询将无济于事,并且mysql_real_escape_string不会涵盖所需的转义。 在这种情况下,您最好通过白名单传递用户的输入,以确保只允许“安全”值。
  • 如果在条件中使用来自用户输入的整数并采用mysql_real_escape_string方法,则您将在下面的注释中遇到Polynomial描述的问题。 这种情况比较棘手,因为整数不会被引号括起来,所以你可以通过验证用户输入只包含数字来处理。
  • 可能还有其他我不知道的情况。 您可能会发现this是一个有用的资源,可以解决您可能遇到的一些更微妙的问题。

使用预准备语句和参数化查询。 这些是由数据库服务器与任何参数分开发送和解析的SQL语句。 这样攻击者就无法注入恶意SQL。

你基本上有两个选项来实现这个目标:

  1. 使用PDO (适用于任何支持的数据库驱动程序):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. 使用MySQLi (用于MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

如果你要连接MySQL以外的数据库,你可以参考一个特定于驱动程序的第二个选项(例如pg_prepare()pg_execute()用于PostgreSQL)。 PDO是通用选项。

正确设置连接

请注意,使用PDO访问MySQL数据库时,默认情况下不使用 实际准备好的语句。 要解决此问题,您必须禁用预准备语句的模拟。 使用PDO创建连接的示例是:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

在上面的例子中,错误模式并不是绝对必要的, 但建议添加它 。 这样,当出现问题时,脚本不会因Fatal Error而停止。 它使开发人员有机会catch任何作为PDOException throw错误。

但是, 强制性的是第一个setAttribute()行,它告诉PDO禁用模拟的预准备语句并使用真实的预准备语句。 这样可以确保PHP在将语句和值发送到MySQL服务器之前不会对其进行解析(使攻击者可能无法注入恶意SQL)。

虽然您可以在构造函数的选项中设置charset ,但重要的是要注意PHP的旧版本(<5.3.6) 默认忽略 DSN中的charset参数

说明

会发生什么是您传递给prepare的SQL语句由数据库服务器解析和编译。 通过指定参数( ?或命名参数,如上例中的:name ),您可以告诉数据库引擎要过滤的位置。 然后,当您调用execute ,预准备语句将与您指定的参数值组合。

这里重要的是参数值与编译语句结合,而不是SQL字符串。 SQL注入通过在创建SQL以发送到数据库时欺骗脚本来包含恶意字符串。 因此,通过将参数中的实际SQL分开发送,可以限制结束您不想要的内容的风险。 使用预准备语句时发送的任何参数都将被视为字符串(尽管数据库引擎可能会进行一些优化,因此参数最终也可能作为数字)。 在上面的例子中,如果$name变量包含'Sarah'; DELETE FROM employees 'Sarah'; DELETE FROM employees结果只是搜索字符串"'Sarah'; DELETE FROM employees" ,你不会得到一个空表

使用预准备语句的另一个好处是,如果在同一个会话中多次执行相同的语句,它只会被解析和编译一次,从而为您带来一些速度提升。

哦,既然你问过如何为插入操作,这是一个例子(使用PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

准备好的语句可以用于动态查询吗?

虽然您仍然可以为查询参数使用预准备语句,但动态查询本身的结构无法进行参数化,并且某些查询功能无法进行参数化。

对于这些特定方案,最好的办法是使用限制可能值的白名单过滤器。

// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}

一些在SQL语句中转义特殊字符的准则。

不要使用MySQL,不推荐使用此扩展,使用MySQLiPDO

库MySQLi

要手动转义字符串中的特殊字符,可以使用mysqli_real_escape_string函数。除非使用mysqli_set_charset设置了正确的字符集,否则该函数将无法正常工作。

例:

$mysqli = new mysqli( 'host', 'user', 'password', 'database' );
$mysqli->set_charset( 'charset');

$string = $mysqli->real_escape_string( $string );
$mysqli->query( "INSERT INTO table (column) VALUES ('$string')" );

要使用mysqli_stmt_bind_param准备语句自动转义值,请使用mysqli_preparemysqli_stmt_bind_param,其中必须提供相应绑定变量的类型以进行适当的转换:

例:

$stmt = $mysqli->prepare( "INSERT INTO table ( column1, column2 ) VALUES (?,?)" );

$stmt->bind_param( "is", $integer, $string );

$stmt->execute();

无论您是使用预处理语句还是mysqli_real_escape_string,您始终必须知道您正在使用的输入数据的类型。

因此,如果使用预准备语句,则必须为mysqli_stmt_bind_param函数指定变量的类型。

正如名称所说,使用mysqli_real_escape_string是为了逃避字符串中的特殊字符,因此它不会使整数安全。此函数的目的是防止破坏SQL语句中的字符串,以及它可能导致的数据库损坏。如果使用得当,mysqli_real_escape_string是一个很有用的函数,特别是与sprintf结合使用时。

例:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647

几年前我写过这个小函数:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

这允许在单行C#-String.Format中运行语句,如:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

考虑到变量类型,它逃脱了。如果您尝试参数化表,列名称,它将失败,因为它将每个字符串放在引号中,这是一个无效的语法。

安全更新:先前str_replace版本允许通过将{#}令牌添加到用户数据中进行注入。preg_replace_callback如果替换包含这些令牌,则此版本不会导致问题。


参数化查询和输入验证是要走的路。 即使使用了mysql_real_escape_string() ,在许多情况下也可能发生SQL注入。

这些示例容易受到SQL注入:

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

要么

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

在这两种情况下,您都不能使用'来保护封装。

Source意外的SQL注入(当转义不够时)


如您所见,人们建议您最多使用预准备语句。 这没有错,但是当每个进程执行一次查询时,会有轻微的性能损失。

我正面临这个问题,但我认为我以非常复杂的方式解决了这个问题 - 黑客用来避免使用引号的方式。 我将它与模拟的预处理语句结合使用。 我用它来防止各种可能的SQL注入攻击。

我的方法:

  • 如果您希望输入为整数,请确保它是真正的整数。 在像PHP这样的可变类型语言中,这非常重要。 您可以使用这个非常简单但功能强大的解决方案: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • 如果你期望整数十六进制的其他东西 。 如果你将它变成十六进制,你将完全逃脱所有输入。 在C / C ++中有一个名为mysql_hex_string()的函数,在PHP中你可以使用bin2hex()

    不要担心转义字符串的原始长度是其原始长度的((2*input_length)+1) ,因为即使您使用mysql_real_escape_string ,PHP也必须分配相同的容量((2*input_length)+1) ,这是相同的。

  • 传输二进制数据时经常使用这种十六进制方法,但我认为没有理由不在所有数据上使用它来防止SQL注入攻击。 请注意,您必须使用0x前置数据或使用MySQL函数UNHEX

所以,例如,查询:

SELECT password FROM users WHERE name = 'root'

会变成:

SELECT password FROM users WHERE name = 0x726f6f74

要么

SELECT password FROM users WHERE name = UNHEX('726f6f74')

Hex是完美的逃脱。 无法注射。

UNHEX函数和0x前缀之间的区别

评论中有一些讨论,所以我最后要说清楚。 这两种方法非常相似,但它们在某些方面略有不同:

** 0x **前缀只能用于char,varchar,text,block,binary等数据列。
此外,如果您要插入空字符串,它的使用有点复杂。 你必须用''完全替换它,否则你会收到错误。

UNHEX()适用于任何列; 你不必担心空字符串。

Hex方法通常用作攻击

请注意,此十六进制方法通常用作SQL注入攻击,其中整数就像字符串一样,只使用mysql_real_escape_string转义。 然后你可以避免使用引号。

例如,如果您只是这样做:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

攻击可以很容易地注入你。 考虑从脚本返回的以下注入代码:

SELECT ... WHERE id = -1 union来自information_schema.tables的所有select table_name

现在只需提取表结构:

SELECT ... WHERE id = -1 union all from information_schema.column select column_name其中table_name = 0x61727469636c65

然后只需选择想要的任何数据。 不是很酷吗?

但是如果可注射部位的编码器会使其十六进制,则无法进行注射,因为查询看起来像这样: SELECT ... WHERE id = UNHEX('2d312075...3635')


我使用三种不同的方法来防止我的Web应用程序容易受到SQL注入的攻击。

  1. 使用的mysql_real_escape_string(),这是一个预先定义的函数PHP,这代码添加反斜杠以下字符:\x00\n\r\'"\x1a。将输入值作为参数传递,以最大限度地减少SQL注入的可能性。
  2. 最先进的方法是使用PDO。

我希望这能帮到您。

请考虑以下查询:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string()不会在这里保护。如果你在查询中的变量周围使用单引号('')可以保护你不受此影响。以下是针对此的解决方案:

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

这个question有一些很好的答案。

我建议,使用PDO是最好的选择。

编辑:

mysql_real_escape_string()自PHP 5.5.0起不推荐使用。使用mysqli或PDO。

mysql_real_escape_string()的替代方法是

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

例:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

我想如果有人想使用PHP和MySQL或其他一些dataBase服务器:

  1. 考虑学习PDO(PHP数据对象) - 它是一个数据库访问层,提供访问多个数据库的统一方法。
  2. 考虑学习MySQLi
  3. 使用本机PHP函数,例如:strip_tagsmysql_real_escape_string()或者如果是变量数字,只需(int)$foo。在here阅读有关PHP中变量类型的更多信息。如果您使用的是PDO或MySQLi等库,请始终使用PDO::quote()mysqli_real_escape_string()

图书馆示例:

---- PDO

-----没有占位符 - 适合SQL注入!这不好

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

-----未命名的占位符

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

-----命名占位符

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

PS

PDO轻松赢得这场战斗。通过支持12种不同的数据库驱动程序和命名参数,我们可以忽略较小的性能损失,并习惯其API。从安全角度来看,只要开发人员以他们应该使用的方式使用它们,它们都是安全的

但是,虽然PDO和MySQLi都非常快,但MySQLi在基准测试中的表现速度要快得多 - 非准备语句约为2.5%,准备语句约为6.5%。

请测试您的数据库的每个查询 - 这是防止注入的更好方法。


无论你最终使用什么,请确保你检查你的输入还没有被magic_quotes或其他一些善意的垃圾所破坏,如果有必要,可以通过stripslashes或其他任何消毒来清理它。


**警告:本回答中描述的方法仅适用于非常特定的场景,并且不安全,因为SQL注入攻击不仅依赖于能够注入X=Y。**

如果攻击者试图通过PHP的$_GET变量或URL的查询字符串来攻击表单,那么如果它们不安全,您将能够捕获它们。

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

因为1=12=21=22=11+1=2,等...都是攻击者的SQL数据库中的常见问题。也许许多黑客应用程序也使用它。

但是您必须小心,不得从您的站点重写安全查询。上面的代码给你一个提示,重写或重定向(这取决于你)特定黑客的动态查询字符串到一个页面,该页面将存储攻击者的IP地址,或者甚至是他们的COOKIES,历史,浏览器或任何其他敏感信息信息,以便您以后可以通过禁止他们的帐户或联系当局来处理它们。


PHP和MySQL有很多答案,但这里是PHP和Oracle的代码,用于防止SQL注入以及经常使用oci8驱动程序:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

一种简单的方法是使用像CodeIgniterLaravel这样的PHP框架,它具有内置功能,如过滤和活动记录,这样您就不必担心这些细微差别。


从安全的角度来看,我赞成存储过程MySQL自5.0以来一直支持存储过程) - 优点是 -

  1. 大多数数据库(包括MySQL)都允许将用户访问限制为执行存储过程。细粒度的安全访问控制对于防止特权攻击升级非常有用。这可以防止受损应用程序直接对数据库运行SQL。
  2. 它们从应用程序中抽象出原始SQL查询,因此应用程序可以使用较少的数据库结构信息。这使人们更难理解数据库的底层结构并设计合适的攻击。
  3. 它们只接受参数,因此参数化查询的优点就在于此。当然 - IMO你仍然需要清理你的输入 - 特别是如果你在存储过程中使用动态SQL。

缺点是 -

  1. 它们(存储过程)很难维护,并且很快就会繁殖。这使得管理它们成为一个问题。
  2. 它们不太适合动态查询 - 如果它们构建为接受动态代码作为参数,则许多优点被否定。

使用这个PHP函数,mysql_escape_string()您可以快速获得良好的预防。

例如:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string - 转义一个字符串以在mysql_query中使用

为了更多预防,您可以在最后添加...

wHERE 1=1   or  LIMIT 1

最后你得到:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1

关于许多有用的答案,我希望在这个帖子中添加一些值。SQL注入是一种攻击,可以通过用户输入(由用户填充然后在查询中使用的输入)完成,SQL注入模式是正确的查询语法,而我们可以调用它:错误的查询由于不好的原因,我们假设有可能试图获取影响安全三原则(机密性,完整性,可用性)的秘密信息(绕过访问控制)的坏人。

现在,我们的观点是防止SQL注入攻击等安全威胁,问题(如何使用PHP防止SQL注入攻击),更真实,数据过滤或清除输入数据是在使用用户输入数据时的情况查询,使用PHP或任何其他编程语言不是这种情况,或者更多的人建议使用现代技术,如准备好的语句或当前支持SQL注入预防的任何其他工具,请考虑这些工具不再可用?您如何保护您的申请?

我对SQL注入的方法是:在将用户输入数据发送到数据库之前清除它(在任何查询中使用它之前)。

数据过滤(将不安全数据转换为安全数据)考虑到PDOMySQLi不可用,您如何保护您的应用程序?你强迫我使用它们吗?除了PHP之外的其他语言呢?我更愿意提供一般性的想法,因为它可以用于更广泛的边界,而不仅仅是特定的语言。

  1. SQL用户(限制用户权限):最常见的SQL操作是(SELECT,UPDATE,INSERT),那么,为什么要给不需要它的用户提供UPDATE权限?例如登录,搜索页面只使用SELECT,那么,为什么在这些具有高权限的页面中使用数据库用户呢?规则:不要为所有权限创建一个数据库用户,对于所有SQL操作,您可以创建方案(deluser,selectuser,updateuser)作为用户名以便于使用。

最小特权原则

  1. 数据过滤:在构建任何查询之前,应验证和过滤用户输入,对于程序员来说,为每个用户输入变量定义一些属性非常重要:数据类型,数据模式和数据长度。对于作为字符串(文本)的字段,必须使用精确规则精确验证(x和y)之间的数字字段:模式就是这种情况,例如,用户名必须只包含一些字符,我们可以说[a- zA-Z0-9_-。]长度在(x和n)之间变化,其中x和n(整数,x <= n)。规则:为我创建精确的过滤器和验证规则是最佳实践。

  2. 使用其他工具:在这里,我也同意你那个准备好的声明(参数化查询)和存储过程,这里的缺点是这些方法需要大多数用户不存在的高级技能,这里的基本思路是区分SQL查询以及在内部使用的数据,即使对于不安全的数据,这两种方法也可以使用,因为这里的用户输入数据不会向原始查询添加任何内容,例如(any或x = x)。有关更多信息,请阅读OWASP SQL注入预防备忘单

现在,如果您是高级用户,请根据需要开始使用此防御,但是,对于初学者,如果他们无法快速实现存储过程并准备语句,则最好尽可能多地过滤输入数据。

最后,让我们考虑用户在下面发送此文本而不是输入他的用户名:

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

可以在没有任何预处理语句和存储过程的情况下提前检查此输入,但为了安全起见,使用它们在用户数据过滤和验证之后启动。

最后一点是检测需要更多努力和复杂性的意外行为; 它不推荐用于普通的Web应用程序。上述用户输入中的意外行为是SELECT,UNION,IF,SUBSTRING,BENCHMARK,SHA,root一旦检测到这些单词,就可以避免输入。

UPDATE1:

一位用户评论说这篇文章没用,好吧!以下是OWASP.ORG提供的内容:

主要防御:

选项#1:准备语句的使用(参数化查询)
选项#2:使用存储过程
选项#3:转义所有用户提供的输入

附加防御:

同时强制执行:最小权限
也执行:白名单输入验证

您可能知道,声称文章应该得到有效论证的支持,至少有一个参考!否则,它被视为攻击和不良声明!

UPDATE2:

从PHP手册,PHP:准备语句 - 手册

转义和SQL注入

绑定变量将由服务器自动转义。服务器在执行之前将其转义值插入到语句模板中的适当位置。必须为服务器提供绑定变量类型的提示,以创建适当的转换。有关更多信息,请参阅mysqli_stmt_bind_param()函数。

服务器内的值的自动转义有时被认为是防止SQL注入的安全功能。如果正确转义输入值,则可以使用非预准备语句实现相同程度的安全性。

UPDATE3:

我创建了一些测试用例,用于了解PDO和MySQLi在使用预准备语句时如何将查询发送到MySQL服务器:

PDO:

$user = "''1''"; //Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

查询日志:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

库MySQLi:

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

查询日志:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

很明显,准备好的声明也在逃避数据,没有别的。

正如上面的声明中所提到的The automatic escaping of values within the server is sometimes considered a security feature to prevent SQL injection. The same degree of security can be achieved with non-prepared statements, if input values are escaped correctly,因此,这证明了数据验证,例如intval()在发送任何查询之前对整数值是一个好主意,此外,在发送查询之前防止恶意用户数据是正确和有效的方法

请查看此问题以获取更多详细信息:PDO向MySQL发送原始查询,而Mysqli发送准备好的查询,两者都产生相同的结果

参考文献:

  1. SQL注入备忘单
  2. SQL注入
  3. 信息安全
  4. 安全原则
  5. 数据验证

如果可能,请转换参数类型。但它只适用于简单类型,如int,bool和float。

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");


有许多方法可以防止SQL注入和其他SQL攻击。您可以在互联网上轻松找到它(谷歌搜索)。当然PDO是一个很好的解决方案。但我想建议你从SQL注入一些良好的链接预防。

什么是SQL注入以及如何防止

用于SQL注入的PHP手册

Microsoft在PHP中对SQL注入和预防的解释

以及其他一些像使用MySQL和PHP预防SQL注入

现在,为什么你需要阻止你的SQL注入查询?

我想告诉您:为什么我们尝试使用以下简短示例来阻止SQL注入:

查询登录验证匹配:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

现在,如果有人(黑客)放弃

$_POST['email']= [email protected]' OR '1=1

和密码什么....

查询将被解析到系统中,最多只能:

$query="select * from users where email='[email protected]' OR '1=1';

另一部分将被丢弃。那么,会发生什么?未经授权的用户(黑客)将能够以管理员身份登录而无需密码。现在,他可以做管理/电子邮件人员可以做的任何事情。请注意,如果不阻止SQL注入,则非常危险。





sql-injection