He estado en la misma situación. También estaba usando declaraciones concatenadas, luego cambié mi aplicación a declaraciones preparadas.
las malas noticias es que va a cambiar cada instrucción SQL creada mediante la concatenación de datos del cliente a la instrucción SQL, que casi serán todas las instrucciones SQL que tenga en sus 50 archivos fuente.
las buenas noticias es la ganancia de cambiar a declaraciones preparadas no tiene precio, por ejemplo:
1-nunca estarás preocupado por algo llamado "ataque de inyección SQL"
el manual dice
Para mí, esa razón -tranquilidad- es suficiente para pagar el costo de cambiar mi código fuente. , ahora sus clientes pueden escribir en un campo de nombre de formulario robert; DROP table students; -- ;)
y te sientes seguro de que nada va a pasar
2- ya no necesita escapar de los parámetros del cliente. puede usarlos directamente en la instrucción SQL, algo como:
$query = "SELECT FROM user WHERE id = ?";
$vars[] = $_POST['id'];
en lugar de
$id = $mysqli->real_escape_string($_POST['id']);
$query = "SELECT FROM user WHERE id = $id";
que es algo que tenía que hacer antes de usar declaraciones preparadas, lo que lo ponía en peligro de olvidarse de escapar de un parámetro como un ser humano normal. y todo lo que necesita un atacante para corromper su sistema es solo 1 parámetro sin escape.
Cambiar el código
Por lo general, cambiar los archivos de origen siempre es arriesgado y doloroso, especialmente si el diseño de su software es malo y si no tiene un plan de prueba obvio. pero te diré lo que hice para que sea lo más fácil posible.
Hice una función que va a usar cada código de interacción con la base de datos, así que puedes cambiar lo que quieras más tarde en un lugar -esa función- puedes hacer algo como esto
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;
}
}
Ahora, puede usar esta interfaz en cualquier lugar que desee en sus archivos fuente, por ejemplo, cambiemos sus declaraciones SQL actuales que ha proporcionado en la pregunta. Cambiemos esto
$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;
}
}
En esto
$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;
}
Sí, el ataque de inyección Sql se aplica concatenando una cadena incorrecta a su declaración SQL. donde es un INSERT
, SELECT
, DELETE
, UPDATE
. por ejemplo
$query = "SELECT * FROM user WHERE name = '{$_GET['name']}' AND password = '{$_GET['pass']}'"
algo así podría ser explotado por
// exmaple.com?name=me&pass=1' OR 1=1; --
lo que resultará en una declaración 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
Buena suerte con el cambio de su software a declaraciones preparadas, y recuerde que la tranquilidad que obtendrá al saber que, pase lo que pase, está a salvo de los ataques de inyección SQL vale la pena el costo de cambiar los archivos fuente