如何防止PHP中的SQL注入?
如果未经修改就将用户输入插入到SQL查询中,则应用程序易受攻击,如以下示例所示:如何防止PHP中的SQL注入?,php,mysql,sql,security,sql-injection,Php,Mysql,Sql,Security,Sql Injection,如果未经修改就将用户输入插入到SQL查询中,则应用程序易受攻击,如以下示例所示: $safe_variable=$_POST['user_input']; mysql_查询(“插入到`table`(`column`)值(`unsafe_variable'); 这是因为用户可以输入类似于value')的内容;升降台--,则查询变为: INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--') 如何防止这种情况发生?
$safe_variable=$_POST['user_input'];
mysql_查询(“插入到`table`(`column`)值(`unsafe_variable');
这是因为用户可以输入类似于value')的内容;升降台--代码>,则查询变为:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
如何防止这种情况发生?我建议使用(PHP数据对象)来运行参数化SQL查询
这不仅可以防止SQL注入,还可以加快查询速度
通过使用PDO而不是mysql
、mysqli
和pgsql
函数,您可以使您的应用程序从数据库中更抽象一点,而很少需要切换数据库提供程序。您可以执行以下基本操作:
$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])
SELECT ... WHERE id = UNHEX('2d312075...3635');
这并不能解决所有问题,但它是一个很好的垫脚石。我遗漏了一些明显的项目,比如检查变量的存在性、格式(数字、字母等)
不推荐的警告:
这个答案的示例代码(与问题的示例代码一样)使用PHP的MySQL
扩展,该扩展在PHP5.5.0中被弃用,在PHP7.0.0中被完全删除
安全警告:此答案不符合安全最佳做法,使用事先准备好的语句。使用下面概述的策略,风险自负。(另外,mysql\u real\u escape\u string()
在PHP7中被删除。)
如果您使用的是最新版本的PHP,下面列出的mysql\u real\u escape\u string
选项将不再可用(尽管mysqli::escape\u string
是一个现代等效选项)。如今,mysql\u real\u escape\u string
选项只适用于旧版本PHP上的遗留代码
您有两种选择—转义不安全变量中的特殊字符
,或使用参数化查询。两者都可以防止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
另请参见函数的详细信息
要使用参数化查询,您需要使用而不是函数。为了重写您的示例,我们需要如下内容
<?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();
?>
您需要了解的关键功能是
此外,正如其他人所建议的,您可能会发现使用以下内容来提升抽象层很有用/更容易
请注意,您询问的案例相当简单,更复杂的案例可能需要更复杂的方法。特别是:
- 如果您想根据用户输入改变SQL的结构,参数化查询将不会有帮助,而且所需的转义不在
mysql\u real\u escape\u string
中。在这种情况下,最好通过白名单传递用户的输入,以确保只允许“安全”值通过
- 如果您在条件中使用用户输入的整数,并采用
mysql\u real\u escape\u string
方法,您将遇到下面注释中描述的问题。这种情况更为棘手,因为整数不会被引号包围,所以您可以通过验证用户输入只包含数字来处理
- 可能还有其他我不知道的案例。您可能会发现,在您可能遇到的一些更微妙的问题上,这是一个有用的资源
使用准备好的语句和参数化查询。这些SQL语句与任何参数分开发送到数据库服务器并由数据库服务器解析。这样,攻击者就不可能注入恶意SQL
您基本上有两个选项来实现这一点:
使用(对于任何受支持的数据库驱动程序):
$stmt=$pdo->prepare('SELECT*FROM employees WHERE name=:name');
$stmt->execute(['name'=>$name]);
foreach($stmt作为$row){
//用$row做点什么
}
使用(用于MySQL):
$stmt=$dbConnection->prepare('SELECT*FROM employees WHERE name=?');
$stmt->bind_参数('s',$name);/'s'指定变量类型=>'string'
$stmt->execute();
$result=$stmt->get_result();
而($row=$result->fetch_assoc()){
//用$row做点什么
}
如果要连接到MySQL以外的数据库,则可以参考特定于驱动程序的第二个选项(例如,PostgreSQL的pg_prepare()
和pg_execute()
)。PDO是通用选项
正确设置连接
请注意,当使用PDO
访问MySQL数据库时,默认情况下不会使用real-prepared语句。要解决此问题,必须禁用对已准备语句的模拟。使用PDO创建连接的示例如下:
$dbConnection=newpdo('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8','user','password');
$dbConnection->setAttribute(PDO::ATTR\u EMULATE\u PREPARES,false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_异常);
在上面的示例中,错误模式并非绝对必要,但建议添加它。这样,当出现错误时,脚本不会因出现致命错误而停止。它使开发人员有机会捕获任何错误,这些错误是抛出的n作为PDOException
s
然而,必需的是第一行setAttribute()
,它告诉PDO禁用模拟的准备语句,并使用真正的准备语句。这确保语句和值不会被解析
$name_bad = "' OR 1'";
$name_bad = mysql_real_escape_string($name_bad);
$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";
$name_evil = "'; DELETE FROM customers WHERE 1 or username = '";
$name_evil = mysql_real_escape_string($name_evil);
$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;
$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`");
$orders = array("name", "price", "qty"); // Field names
$key = array_search($_GET['sort'], $orders)); // if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically.
$query = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe
$orderby = white_list($_GET['orderby'], "name", ["name","price","qty"], "Invalid field name");
$query = "SELECT * FROM `table` ORDER BY `$orderby`"; // sound and safe
$unsafe_variable = $_POST['user_id'];
$safe_variable = (int)$unsafe_variable ;
mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";
$_POST['email']= admin@emali.com' OR '1=1
$query="select * from users where email='admin@emali.com' OR '1=1';
SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'
wHERE 1=1 or LIMIT 1
SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1
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");
$count = DB::column('SELECT COUNT(*) FROM `user`);
$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);
$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));
$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));
SELECT password FROM users WHERE name = 'root';
SELECT password FROM users WHERE name = 0x726f6f74;
SELECT password FROM users WHERE name = UNHEX('726f6f74');
"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])
SELECT ... WHERE id = -1 UNION ALL SELECT table_name FROM information_schema.tables;
SELECT ... WHERE id = -1 UNION ALL SELECT column_name FROM information_schema.column WHERE table_name = __0x61727469636c65__;
SELECT ... WHERE id = UNHEX('2d312075...3635');
$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)");
$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();
GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;
select * from mysql.user where User='username';
[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'
$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
$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
RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php
$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')");
$stmt = $mysqli->prepare("INSERT INTO table (column1, column2) VALUES (?,?)");
$stmt->bind_param("is", $integer, $string);
$stmt->execute();
$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
$unsafe_variable = mysql_real_escape_string($_POST['user_input']);
$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');
$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');
$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);
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;
}
runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);
$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();
$user->first_name = 'Jamie';
$user->save();
$tweets = ORM::for_table('tweet')
->select('tweet.*')
->join('user', array(
'user.id', '=', 'tweet.user_id'
))
->where_equal('user.username', 'j4mie')
->find_many();
foreach ($tweets as $tweet) {
echo $tweet->text;
}