Intereting Posts
MySQL Создать триггер Вставить содержимое в другую таблицу из одной базы данных Как преобразовать base64 в zip и перейти на сервер Синхронизация большой локальной базы данных с серверной БД (MySQL) Emoji для кодирования JSON, сообщение на веб-сервер Черный список слов по содержимому для фильтрации сообщения Загрузка Multifile не работает PHP с использованием mcrypt и хранения зашифрованных в MySQL Предупреждение: mysqli_stmt :: bind_result (): Число переменных привязки не совпадает с числом полей в подготовленной ошибке оператора Ошибка PDO: «Недопустимый номер параметра: параметр не определен» Ошибка phpmysql – # 1273 – # 1273 – Неизвестная сортировка: 'utf8mb4_general_ci' как отправить HTML-письмо с встроенным прикрепленным изображением с помощью PHP Как шифровать данные в javascript и расшифровывать в php? Как связать модель в CakePHP по полям, не названным по соглашению? Предупреждение: mysqli_connect (): Неизвестный сервер сервера MySQL PHP получает доступ к переменным $ _POST в массив?

Переход к подготовленным заявкам

Я только начал работать над своим первым проектом (для удовольствия). Я изучаю PHP и MySQL и имею свое завершенное мое первое рабочее приложение. Он работает, но теперь я изучаю, как защитить свое приложение и тем самым предотвратить инъекции SQL. У меня есть примерно 50+ PHP-файлов, которые управляют взаимодействием с моей базой данных MySQL. Все они выглядят примерно так:

<?php $inputvalues = $_POST; $errors = false; $result = false; session_start(); $uid = $_SESSION['usr_id']; $mysqli = new mysqli('localhost', "root", "", "testdb"); if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); } foreach ($inputvalues as $key => $value) { if(isset($value) && !empty($value)) { $inputvalues[$key] = $mysqli->real_escape_string( $value ); } else { $errors[$key] = 'The field '.$key.' is empty'; } } if( !$errors ) { $addresult = " SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '".$inputvalues['schoolid']."' "; if( $result = $mysqli->query($addresult) ) { while($row = $result->fetch_all()) { $returnResult = $row; } } } mysqli_close($mysqli); echo json_encode(['result' => $returnResult, 'errors' => $errors]); exit; ?> 

Это формат, который я использовал во время моего приложения для чтения и записи данных в / из базы данных. Если мне нужно изменить их на подготовленные заявления, где я не вставляю какую-либо информацию, а просто получаю их, как бы мне это сделать?

Кроме того, если я не вхожу ни в какие данные в БД, он все еще уязвим для инъекций?

Не могли бы вы предоставить мне пример того, как я могу адаптировать мой текущий код к подготовленным заявлениям, я бы очень признателен.

Related of "Переход к подготовленным заявкам"

Я был в той же ситуации. Я также использовал конкатенированные утверждения, затем я переключил свое приложение на подготовленные заявления.

плохая новость заключается в том, что вы собираетесь изменить каждый оператор SQL, созданный путем объединения данных клиента в оператор SQL, что почти будет каждым оператором SQL, который у вас есть в ваших 50 исходных файлах.

хорошая новость – выигрыш от перехода к подготовленным заявлениям бесценен, например:

1 – вы никогда не будете беспокоиться о чем-то, называемом «SQL Injection attack»,

в руководстве php говорится:

Если приложение использует исключительно подготовленные операторы, разработчик может быть уверен, что SQL-инъекция не произойдет (однако, если другие части запроса создаются с несвязанным вводом, SQL-инъекция по-прежнему возможна).

Для меня эта причина – образ ума – достаточно, чтобы заплатить стоимость изменения моего исходного кода. , теперь ваши клиенты могут ввести имя поля формы robert; DROP table students; -- ;) robert; DROP table students; -- ;) robert; DROP table students; -- ;) и вы чувствуете себя в безопасности, что ничего не случится

2- вам больше не нужно выходить из параметров клиента. вы можете напрямую использовать их в инструкции SQL, например:

 $query = "SELECT FROM user WHERE id = ?"; $vars[] = $_POST['id']; 

вместо

 $id = $mysqli->real_escape_string($_POST['id']); $query = "SELECT FROM user WHERE id = $id"; 

что вам нужно было сделать, прежде чем использовать подготовленные заявления, что помешало вам забыть избежать одного параметра как обычного человека. и все, что требуется для того, чтобы злоумышленник мог испортить вашу систему, – это всего лишь 1 неэкранированный параметр.


Изменение кода

обычно изменение исходных файлов всегда опасно и имеет боль, особенно если ваш дизайн программного обеспечения плох и если у вас нет очевидного плана тестирования. но я скажу вам, что я сделал, чтобы сделать его как можно проще.

Я сделал функцию, которую будет использовать каждый код взаимодействия с базой данных, поэтому вы можете изменить то, что хотите позже, в одном месте – эта функция – вы можете сделать что-то вроде этого

 class SystemModel { /** * @param string $query * @param string $types * @param array $vars * @param \mysqli $conn * @return boolean|$stmt */ public function preparedQuery($query,$types, array $vars, $conn) { if (count($vars) > 0) { $hasVars = true; } array_unshift($vars, $types); $stmt = $conn->prepare($query); if (! $stmt) { return false; } if (isset($hasVars)) { if (! call_user_func_array(array( $stmt, 'bind_param'), $this->refValues($vars))) { return false; } } $stmt->execute(); return $stmt; } /* used only inside preparedQuery */ /* code taken from: https://stackoverflow.com/a/13572647/5407848 */ protected function refValues($arr) { if (strnatcmp(phpversion(), '5.3') >= 0) { $refs = array(); foreach ($arr as $key => $value) $refs[$key] = &$arr[$key]; return $refs; } return $arr; } } 

Теперь вы можете использовать этот интерфейс в любом месте в исходных файлах, например, давайте сменим текущие SQL-запросы, предоставленные вами в вопросе. Давайте изменим это

 $mysqli = new mysqli('localhost', "root", "", "testdb"); $addresult = " SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '".$inputvalues['schoolid']."'"; if( $result = $mysqli->query($addresult) ) { while($row = $result->fetch_all()) { $returnResult = $row; } } 

В это

 $mysqli = new mysqli('localhost', "root", "", "testdb"); $sysModel = new SystemModel(); $addresult = " SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = ?"; $types = "i"; // for more information on paramters types, please check : //https://php.net/manual/en/mysqli-stmt.bind-param.php $vars = []; $vars[] = $inputvalues['schoolid']; $stmt = $sysModel->preparedQuery($addresult, $types, $vars, $mysqli); if (!$stmt || $stmt->errno) { die('error'); // TODO: change later for a better illustrative output } $result = $stmt->get_result(); $returnResult = []; while ($row = $result->fetch_array(MYSQLI_ASSOC)) { $returnResult[] = $row; } 

Кроме того, если я не вхожу ни в какие данные в БД, он все еще уязвим для инъекций?

Да, атака Sql Injection применяется путем объединения плотной строки в ваш оператор SQL. где это INSERT , SELECT , DELETE , UPDATE . например

 $query = "SELECT * FROM user WHERE name = '{$_GET['name']}' AND password = '{$_GET['pass']}'" 

что-то подобное можно использовать

 // exmaple.com?name=me&pass=1' OR 1=1; -- 

что приведет к выражению SQL

 $query = "SELECT * FROM user WHERE name = 'me' AND password = '1' OR 1=1; -- '" //executing the SQL statement and getting the result if($result->num_rows){ //user is authentic }else{ //wrong password } // that SQL will always get results from the table which will be considered a correct password 

Удачи в том, чтобы переключить свое программное обеспечение на подготовленные заявления и помните, что душевное спокойствие, которое вы получите, зная, что, что бы ни случилось, вы безопасны от атак SQL-инъекций, стоит затрат на изменение исходных файлов