I had wanted to see the merits of dynamically and "automatically" applying mysql_real_escape_string() to $_GET and $_POST as arrays rather than manually each time I would type up a query. I used array_walk_recursive() to call my function "mysql_safe" to apply mysql_real_escape_string() to each key of the $_GET and $_POST arrays.
My function is part of a class, and it is called each time I connect to the database to perform a query:
<?php
class MyClass {
function mysql_safe ( $item, $key, $type )
{
switch( $type )
{
case 'get':
if( count( $_GET ) > 0 )
{
$_GET[$item] = mysql_real_escape_string( $key );
}
break;
case 'post':
if( count( $_POST ) > 0 )
{
$_POST[$item] = mysql_real_escape_string( $key );
}
break;
}
}
function safe_get ( )
{
#Flag to only run function once
if( $this->get_flag == true ) { return true; }
array_walk_recursive( $_GET, array( $this, 'mysql_safe' ), 'get' );
array_walk_recursive( $_POST, array( $this, 'mysql_safe' ), 'post' );
$this->get_flag = true;
}
}
?>
However, after using this function, I find that it does indeed work, it also creates new $_GET and $_POST values in which $item and $key are swapped. So I end up with $_GET[$item] = $key, as well as $_GET[$key] = $item. I have not yet determined if this is due to the actual coding itself, or my particular method of implementation.
mysql_real_escape_string
(PHP 4 >= 4.3.0, PHP 5)
mysql_real_escape_string — Escapa caracteres especiales de una cadena para su uso en una sentencia SQL
Descripción
Escapa todos los caracteres especiales en la cadena_no_escapada , tomando en cuenta el juego de caracteres actual de la conexión, de tal modo que sea seguro usarla con mysql_query(). Si se van a insertar datos binarios, debe usarse esta función.
mysql_real_escape_string() llama a la función de la biblioteca MySQL mysql_real_escape_string, la cual coloca barras invertidas antes de los siguientes caracteres: \x00, \n, \r, \, ', " y \x1a.
Esta función debe usarse siempre (con algunas excepciones) para garantizar que los datos sean seguros antes de enviar una consulta a MySQL
Lista de parámetros
- cadena_no_escapada
-
La cadena a ser escapada.
- link_identifier
-
The MySQL connection. If the link identifier is not specified, the last link opened by mysql_connect() is assumed. If no such link is found, it will try to create one as if mysql_connect() was called with no arguments. If by chance no connection is found or established, an E_WARNING level error is generated.
Valores retornados
Devuelve la cadena escapada, o FALSE en caso de que ocurra un error.
Ejemplos
Example #1 Ejemplo sencillo de mysql_real_escape_string()
<?php
// Conectarse
$enlace = mysql_connect('mysql_host', 'mysql_usuario', 'mysql_contrasenya')
OR die(mysql_error());
// Consulta
$query = sprintf("SELECT * FROM usuarios WHERE usuario='%s' AND
password='%s'",
mysql_real_escape_string($usuario),
mysql_real_escape_string($password));
?>
Example #2 Un ejemplo de un ataque de inyección SQL
<?php
// Consultar la base de datos para verificar si hay una coincidencia de usuario
$consulta = "SELECT * FROM usuarios WHERE usuario='{$_POST['username']}' AND password='{$_POST['password']}'";
mysql_query($consulta);
// No revisamos $_POST['password'], ¡podría ser cualquier cosa que el usuario
// quiera! Por ejemplo:
$_POST['username'] = 'aidan';
$_POST['password'] = "' OR ''='";
// Esto quiere decir que la consulta enviada a MySQL sería:
echo $consulta;
?>
La consulta enviada a MySQL:
SELECT * FROM usuarios WHERE usuario='aidan' AND password='' OR ''=''
Esto permitiría que cualquiera iniciara una sesión sin una contraseña válida.
Example #3 Una consulta "Recomendable"
Mediante el uso de mysql_real_escape_string() sobre cada variable se previene la inyección de SQL. Este ejemplo demuestra el método "recomendable" para ejecutar una consulta en la base de datos, independientemente del valor de las Comillas Mágicas.
<?php
if (isset($_POST['nombre_producto']) && isset($_POST['descripcion_producto']) && isset($_POST['id_usuario'])) {
// Conectarse
$enlace = mysql_connect('host_mysql', 'usuario_mysql', 'contrasenya_mysql');
if(!is_resource($enlace)) {
echo "Falló la conexión con el servidor\n";
// ... registrar el error apropiadamente
} else {
// Revertir los efectos de magic_quotes_gpc/magic_quotes_sybase sobre las variables si es necesario.
if(get_magic_quotes_gpc()) {
$nombre_producto = stripslashes($_POST['nombre_producto']);
$descripcion_producto = stripslashes($_POST['descripcion_producto']);
} else {
$nombre_producto = $_POST['nombre_producto'];
$descripcion_producto = $_POST['descripcion_producto'];
}
// Hacer una consulta segura
$consulta = sprintf("INSERT INTO productos (`nombre`, `descripcion`, `id_usuario`) VALUES ('%s', '%s', %d)",
mysql_real_escape_string($nombre_producto, $enlace),
mysql_real_escape_string($descripcion_producto, $enlace),
$_POST['id_usuario']);
mysql_query($consulta, $enlace);
if (mysql_affected_rows($enlace) > 0) {
echo "Producto insertado\n";
}
}
} else {
echo "Llene los campos del formulario correctamente\n";
}
?>
La consulta no se ejecutará correctamente ahora, y los ataques de inyección de SQL no funcionarán.
Notes
Note: Es necesaria una conexión MySQL antes de usar mysql_real_escape_string() o de lo contrario un error de nivel E_WARNING es generado, y FALSE es devuelto. Si id_enlace no está definido, se usará la última conexión con MySQL.
Note: Si se habilita magic_quotes_gpc, aplique stripslashes() sobre los datos primero. Usar esta función sobre datos que ya han sido escapados los escapará dos veces.
Note: Si esta función no es usada para escapar datos, la consulta es vulnerable a Ataques de Inyección de SQL.
Note: mysql_real_escape_string() no escapa % ni _. Éstos son comodines en MySQL si se combinan con LIKE, GRANT, o REVOKE.
Ver también
- mysql_client_encoding() - Devuelve el nombre del juego de caracteres
- addslashes() - Escapa una cadena insertando barras "\"
- stripslashes() - Desmarca la cadena marcada con addslashes
- La directiva magic_quotes_gpc
- La directiva magic_quotes_runtime
mysql_real_escape_string
12-Jul-2009 06:06
13-Jun-2009 07:37
I always use this function so I don't have to retype over and over the mysql_real_escape_string function.
<?php
function safe($value){
return mysql_real_escape_string($value);
}
?>
Then, when I am using my code, I simply use:
<?php
$name = safe($_POST["name"]);
$password = safe($_POST["password"]);
?>
25-Mar-2009 09:07
<?php
// Here is a simple named binding function for queries that makes SQL more readable:
// $sql = "SELECT * FROM users WHERE user = :user AND password = :password";
// mysql_bind($sql, array('user' => $user, 'password' => $password));
// mysql_query($sql);
function mysql_bind(&$sql, $vals) {
foreach ($vals as $name => $val) {
$sql = str_replace(":$name", "'" . mysql_real_escape_string($val) . "'", $sql);
}
}
?>
24-Mar-2008 07:46
This function won't help you when inserting binary data, to me it will get mallformed into the database. Probably UTF-8 combinations will be translated by this function or somewhere else when inserting data when running mysql in UTF-8 mode.
A better way to insert binary data is to transfer it to hexadecimal notation like this example:
<?php
$string = $_REQUEST['string'];
$binary = file_get_contents($_FILE['file']['tmp_name']);
$string = mysql_real_escape_string($string);
$binary_hex = bin2hex($binary);
$query = "INSERT INTO `table` (`key`, `string`, `binary`, `other`) VALUES (NULL, '$string', 0x$binary_hex, '$other')";
?>
03-Mar-2008 06:57
My escape function:
Automatically adds quotes (unless $quotes is false), but only for strings. Null values are converted to mysql keyword "null", booleans are converted to 1 or 0, and numbers are left alone. Also can escape a single variable or recursively escape an array of unlimited depth.
<?php
function db_escape($values, $quotes = true) {
if (is_array($values)) {
foreach ($values as $key => $value) {
$values[$key] = db_escape($value, $quotes);
}
}
else if ($values === null) {
$values = 'NULL';
}
else if (is_bool($values)) {
$values = $values ? 1 : 0;
}
else if (!is_numeric($values)) {
$values = mysql_real_escape_string($values);
if ($quotes) {
$values = '"' . $values . '"';
}
}
return $values;
}
?>
27-Dec-2007 09:49
In response to Michael D - DigitalGemstones.com:
Check the example again: sprintf(%d) already does the int conversion for you, so it's both perfectly save as well as more elegant than manually casting.
28-Aug-2007 12:16
if you're doing a mysql wildcard query with
LIKE, GRANT, or REVOKE
you may use addcslashes to escape the string:
<?php
$param = mysql_real_escape_string($param);
$param = addcslashes($param, '%_');
?>
06-Sep-2006 04:25
mysql_real_escape_string is a bit annoying when you need to do it over an array.
<?php
function mysql_real_escape_array($t){
return array_map("mysql_real_escape_string",$t);
}
?>
this one just mysql_real_escape's the whole array.
ex) <?php $_POST=mysql_real_escape_array($_POST); ?>
and then you dont have to worry about forgetting to do this.
18-Jul-2006 08:19
It seems to me that you could avoid many hassels by loading valid database values into an array at the beginning of the script, then instead of using user input to query the database directly, use it to query the array you've created. For example:
<?php
//you still have to query safely, so always use cleanup functions like eric256's
$categories = sql_query("select catName from categories where pageID = ?",$_GET['pageID']);
while ($cts = @mysql_fetch_row($categories)) {
//making $cts both the name and the value of the array variable makes it easier to check for in the future.
//obviously, this naming system wouldn't work for a multidimensional array
$cat_ar[$cts[0]] = $cts[0];
}
...
//user selects sorting criteria
//this would be from a query string like '?cats[]=cha&cats[]=fah&cats[]=lah&cats[]=badValue...', etc.
$cats = $_GET['cats'];
//verify that values exist in database before building sorting query
foreach($cats as $c) {
if ($cat_ar[$c]) { //instead of in_array(); maybe I'm just lazy... (see above note)
$cats1[] = "'".mysql_real_escape_string($c)."'";
}
}
$cats = $cats1;
//$cats now contains the filtered and escaped values of the query string
$cat_query = '&& (category_name = \''.implode(' || category_name = \'',$cats).'\')';
//build a sql query insert
//$cat_query is now "&& (category_name = 'cha' || category_name = 'fah' || category_name = 'lah')" - badValue has been removed
//since all values have already been verified and escaped, you can simply use them in a query
//however, since $pageID hasn't been cleaned for this query, you still have to use your cleaning function
$items = sql_query("SELECT * FROM items i, categories c WHERE i.catID = c.catID && pageID = ? $cat_query", $pageID);
30-May-2006 08:38
Note that mysql_real_escape_string doesn't prepend backslashes to \x00, \n, \r, and and \x1a as mentionned in the documentation, but actually replaces the character with a MySQL acceptable representation for queries (e.g. \n is replaced with the '\n' litteral). (\, ', and " are escaped as documented) This doesn't change how you should use this function, but I think it's good to know.
