downloads | documentation | faq | getting help | mailing lists | licenses | wiki | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

Reportando errores> <Modelo de almacenamiento encriptado
Last updated: Fri, 20 May 2011

view this page in

Inyección de SQL

Muchos desarrolladores web son desprevendios de cómo las consultas SQL pueden ser manipuladas, y asumen que una consulta SQL es un comando confiable. Esto significa que las consultas SQL están expuestas a que sean malversadas en controles de acceso, y por lo tanto, sobrepasar las revisiones de autenticación y autorización estándar, y que algunas veces las consultas SQL aún podrían permitir el acceso de comandos a nivel de sistema operativo del ordenador.

Comandos directos de Inyección SQL es una técnica donde un atacante crea o altera comandos SQL existentes para exponer datos ocultos, sobreponerse a los que son importantes, o peor aún, ejecutar comandos peligrosos a nivel de sistema en el equipo donde se encuentra la base de datos. Esto se logra a través de la aplicación, tomando la entrada del usuario y combinándola con parámetros estáticos para elaborar una consuta SQL. Los siguientes ejemplos están basados en historias reales, desafortunadamente.

Debido a la falta de validación en la entrada de datos y conectándose a la base de datos con privilegios de super usuario o de alguien con privilegios que puede crear usuarios, el atacante podría crear un super usuario en su base de datos.

Example #1 Dividiendo el conjunto de resultados en páginas ... y haciendo super usuarios (PostgreSQL)

<?php

$offset 
$argv[0]; // Cuidado, no hay validación en la entrada de datos!
$query  "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
$result pg_query($conn$query);

?>
Los usuarios normales dan clic en los enlaces 'siguiente' o 'atras' donde $offset está codificado en la URL. El script espera que el $offset entrante sea un número décimal. Sin embargo, qué pasa si alguien intenta irrumpir añadiendo una función urlencode() al formulario de la siguiente URL
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
    select 'crack', usesysid, 't','t','crack'
    from pg_shadow where usename='postgres';
--
Si esto sucedió, entonces el script podría presentarle un acceso de super usuario al atacante. Nótese que 0; es para proveer un offset válido a la consulta original y para finalizarla.

Note:

Esta es una técnica común para forzar al analizador SQL a que ignore el resto de la consulta escrita por el desarrollador con dos guiónes: -- los cuales representan un comentario en SQL.

Una forma factible de obtener contraseñas es burlar las páginas de búsqueda de resultados. Lo único que el atacante necesita hacer es ver si hay variables que hayan sido enviadas y sean usadas en declaraciones SQL las cuales no sean manejadas apropiadamente. Esos filtros pueden ser puestos comunmente en un formulario anterior para personalizar las cláusulas WHERE, ORDER BY, LIMIT y OFFSET en las declaraciones SELECT. Si su base de datos soporta el constructor UNION, el atacante podría intentar añadir una consulta enetera a la consulta original para listar contraseñas de una tabla arbitraria. Utilizar campos de contraseña encriptadoslds es fuertemente recomendado.

Example #2 Listando nuestros artículos ... y algunas contraseñas (de cualquier servidor de base de datos)

<?php

$query  
"SELECT id, name, inserted, size FROM products
                  WHERE size = '
$size'
                  ORDER BY 
$order LIMIT $limit$offset;";
$result odbc_exec($conn$query);

?>
La parte estática de la consulta puede ser combinada con otra declaración SELECT la cual revela todas las contraseñas:
'
union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable;
--
Si esta consulta (ejecutándose con ' y --) fuera asignada a una de las variables utilizadas en $query, la consulta reaccionará bestialmente.

Las consultas de actualización de SQL, también son susceptibles a ataques. Estas consultas también son amenazadas por acortamiento y adición en una consulta completamente nueva a esta. Sin embargo el atacante podría manipularla con la cláusula SET. En este caso, algunos esquemas de información deben ser procesados para manipular la consulta exitosamente. Este puede adquirirse examinando la forma de nombres de las variables, o simplemente forzarlo con un ataque de fuerza bruta. No hay muchas convenciones de nombres para campos que almacenan contraseñas o nombres de usuarios.

Example #3 Desde re-establecer una contraseña ... hasta ganar más privilegios (en cualquier servidor de bases de datos)

<?php
$query 
"UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
Pero un usuario malicioso podría enviar el valor con ' or uid like'%admin%'; -- hacia $uid para cambiar la contraseña del administrador, o simplemente cambiar $pwd a "hehehe', admin='yes', trusted=100 " (con un espacio al final) para ganar más privilegios. Entonces la consulta será cambiada así:
<?php

// $uid == ' or uid like'%admin%'; --
$query "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --";

// $pwd == "hehehe', admin='yes', trusted=100 "
$query "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE
...;"
;

?>

Un ejemplo horrible de cómo pueden ser accedidos los comandos a nivel de sistema operativo en algunos hospedadores de bases de datos.

Example #4 Atacando el sistema operativo que hospeda la base de datos (Servidor MSSQL)

<?php

$query  
"SELECT * FROM products WHERE id LIKE '%$prod%'";
$result mssql_query($query);

?>
Si un atacante envía el valor a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- hacia $prod, la consulta $query será:
<?php

$query  
"SELECT * FROM products
                    WHERE id LIKE '%a%'
                    exec master..xp_cmdshell 'net user test testpass /ADD'--"
;
$result mssql_query($query);

?>
El servidor MSSQL ejecuta la sentencia SQL en el lote que incluye un comando para añadir un usuario nuevo a la base de datos de cuentas locales. Si esta aplicación estuviera ejecutándose como sa, y el servicio MSSQLSERVER se está ejecutando con los privilegios suficientes, el atacante ahora podría tener una cuenta con la cual tendría acceso a esta máquina.

Note:

Algunos de los ejemplos de citados arriba estan vinculados a un servidor de base de datos específico. Esto no significa que un ataque similar sea imposible en contra de otros productos. Su servidor de base de datos podría ser vulnerable de forma similar en otra manera.

Técnicas de evación

Podría decir que el atacante debe poseer una proción de información sobre el esquema de la base de datos en la mayoría de los ejemplos. Tienes razón, pero nunca sabe cuando y cómo esto pueda ser tomado, y si sucede, su base de datos podría ser expuesta. Si está usando una base de datos de fuente abierta, o un paquete disponible publicamente, el cual podría pertenecer a un sistema de manejo de contenido o un foro, los intrusos reproducen facilmente una copia de una porción de su código lo cual podría ser un riesgo de seguridad si esta está pobremente diseñada.

Esos ataques están principalmente basados en explotar el código que no ha sido escrito teniendo en mente la seguridad. Nunca confíes en ningún tipo de entrada, especialmente la que viene del lado del cliente, aún cuando esta venga de una caja de selección, un campo oculto o una cookie. El primer ejemplo muestra que una inofensiva consulta puede causar desastres.

  • Nunca se conecte como super usuario o como el propietario de la base de datos. Siempre utilice usuarios personalizados con privilegios muy limitados.
  • Revise si la entrada proporcionada tiene el tipo de datos que se espera. PHP tiene un rango amplio de funciones para validar la entrada de datos, desde las más simples encontradas en Funciones de variable y en Funciones de tipo Caracter (Ej. is_numeric(), ctype_digit() respectivamente) y siguiendo el apoyo con las Expresiones regulares compatibles con Perl.
  • Si la expresión espera una entrada numérica, considere verificar los datos con la función is_numeric(), o silenciosamente cambie su tipo utilizando settype(), o use su representación numérica por medio de sprintf().

    Example #5 Una forma más segura de redactar una consulta para paginación

    <?php

    settype
    ($offset'integer');
    $query "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";

    // Fíjese en %d en el formato de cadena, utilizar %s podría no tener un resultado significativo
    $query sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
                     
    $offset);

    ?>

  • Encierre entre comillas cada valor no-numérico provisto por el usuario que sea pasado a la base de datos filtrado con la función de cadena específica de la base de datos (Ej. mysql_real_escape_string(), sqlite_escape_string(), etc.). Si una función de escape (o de filtrado) de cadena específica de la base de datos, o un mecanismo similar no está disponible, las funciones addslashes() y str_replace() podrían ser útiles (dependiendo del tipo de la base de datos). Vea el primer ejemplo. Como lo muestra el ejemplo, agregar comillas a la parte estática de la consulta no es suficiente, lo que hace que esta consulta sea facilmente vulnerada.
  • No muestre ninguna información específica de la base de datos, especialmente sobre el esquema, por su correcto significado es como jugar sucio contra usted mismo. Vea también Reporte de errores y Manejo de errores y funciones de registro.
  • Podría utilizar procedimientos almacenados y previamente cursores definidos, para abstraer el acceso a datos para que los usuarios no tengan acceso directo a las tablas o vistas, para que esta solución tenga otros impactos.

Junto a esto, usted se beneficia de tener un registro de las consultas ya sea dentro de su script o de la base de datos en si misma, si es que esta soporta el registro. Obviamente, llevar un registro no le previene cualquier intento de daño, pero éste puede ser útil para hacer una retro revisión de cual aplicación ha sido intervenida. El registro no es útil por sí mismo, pero lo es debido a la información que contiene. Más detalles generalmente es mejor que los pocos.



Reportando errores> <Modelo de almacenamiento encriptado
Last updated: Fri, 20 May 2011
 
add a note add a note User Contributed Notes Inyección de SQL
kirby4 at live dot ca 19-Feb-2011 10:35
A good way to counter SQL injection for queries of type SELECT is use hash function on data by PHP and the database server.
For example, it is possible to use the MySQL function MD5 () to produce a hash of data-server side , and the equivalent function in php web-server side.

<?php
$login
= mysql_query("select f_uname, f_passwd from t_user where MD5(f_uname) = '".md5($uname)."' and MD5(f_passwd)='".md5($passwd)."'");
?>

Thus, the injected requests will be crushed and it will become much more difficult to obtain data in the database. Use both sides of the hash result in a comparison of hash, not the execution of the injected queries.

Unfortunately, it probably does not work with other types of queries.
smunday at visionaryweb dot com 17-Dec-2010 10:58
Another suggestion would be to build a series of DB procedures / functions that you give the DB user access to to manipulate or select data. That way, all input would run through this exposed interface and all parameters are forced to be typecast (or rejected).
Anonymous 27-Sep-2010 04:16
Pangolin is an automatic SQL injection penetration testing tool developed by NOSEC.
Its goal is to detect and take advantage of SQL injection vulnerabilities on web applications. Once it detects one or more SQL injections on the target host, the user can choose among a variety of options to perform an extensive back-end database management system fingerprint, retrieve DBMS session user and database, enumerate users, password hashes, privileges, databases, dump entire or user"s specific DBMS tables/columns, run his own SQL statement, read specific files on the file system and more.
wang dot liang dot com at gmail dot com 11-Mar-2010 11:11
another way to stop sql injection when you odbc_*: create two users,
one has only select permission,
the other has only delete, update, and insert permission,

so you can use select-only user to call odbc_exec while you don't have to check the sql injection; and you use d/u/i only user to update database by calling odbc_prepare and odbc_execute.
fyrye 06-Aug-2009 10:59
Another way to prevent SQL injections as opposed to binary, is to use URL Encoding or Hex Encoding.
I haven't seen a complete example of stopping SQL Injections, most refer to use the mysql_real_escape_string function or param statements.

Several examples at http://en.wikipedia.org/wiki/SQL_injection

Which will stop \x00, \n, \r, \, ', " and \x1a based attacks.
Alot depends on your SQL query structure, though vector level attacks are still viable.

Other than that build your own regex replacement to protect specific queries that could alter or compromise your database/results for specific sections of your processing pages.
Also use unique table and field names. Not just putting _ infront of them...
Example, don't store User/s or Customer/s information in a table named the same.
And NEVER use the same form field names for database field names.
bendikt [at] armed [dot] nu 27-Jul-2009 02:31
i just played around with the array_walk function.
It suddenly struck me that almost all super globals are arrays.
So what i discovered was that i can apply the array_walk function to the super globals. Doing so you automatically run a function call through the super globals
With this piece of code i wrote you should be able to secure most of you input data.

<?php

class secure
{
    function
secureSuperGlobalGET(&$value, $key)
    {
       
$_GET[$key] = htmlspecialchars(stripslashes($_GET[$key]));
       
$_GET[$key] = str_ireplace("script", "blocked", $_GET[$key]);
       
$_GET[$key] = mysql_escape_string($_GET[$key]);
        return
$_GET[$key];
    }
   
    function
secureSuperGlobalPOST(&$value, $key)
    {
       
$_POST[$key] = htmlspecialchars(stripslashes($_POST[$key]));
       
$_POST[$key] = str_ireplace("script", "blocked", $_POST[$key]);
       
$_POST[$key] = mysql_escape_string($_POST[$key]);
        return
$_POST[$key];
    }
       
    function
secureGlobals()
    {
       
array_walk($_GET, array($this, 'secureSuperGlobalGET'));
       
array_walk($_POST, array($this, 'secureSuperGlobalPOST'));
    }
}

?>
Note that you can modify this in anyway to suit your needs.
The Script has been tested.
Quietust 03-Jun-2009 12:23
An anonymous comment below suggests using PEAR with prepare() / execute() - though it was posted 3 years ago, it is still true today, though it's even easier now since PDO is included in most distributions. For SET/WHERE clauses and INSERT statements, just prepare the query with question marks in place of the dynamic parameters, bind each value in, then execute it, and it'll do all of the escaping for you, rendering the query immune to injection. Dynamic substitution of ORDER BY or LIMIT clauses has to be done the old fashioned way, though, so you still need to be careful with those.

Even without PDO, if you're using Postgres, you've already got the means to use parameterized queries, and if you're using MySQL, you simply need to ignore the outdated "mysql" extension and use "mysqli" instead.
fOV 12-Nov-2008 12:53
The best prevention is to deactivate master..xp_cmdshell.
Run in isql the command `sp_configure 'xp_cmdshell''
If the value for "config value" is 1 then make in the isql
`sp_configure 'xp_cmdshell',0'.
If you want see the options from your mssql-Server make
`sp_configure 'show advanced options',1'
and then `sp_configure'
jaimthorn at yahoo dot com 13-Oct-2008 09:32
dark dot avenger at email dot cz wrote:

"I think that easy way to protect against SQL injection is to convert inputted data into binary format, so that whatever input is, in sql query it will consist only of 1s and 0s."

Unless there is a 1-to-1 correspondence between your inputted data and the characters in your 'binary' format, a SELECT query wouldn't work anymore.  Not a binary format, but it makes my point: MIME encoding the text 'Dark Avenger' results in 'RGFyayBBdmVuZ2Vy'.  If I wanted to look up anyone with 'Avenger' in his/her name, then 'Avenger' would be encoded as 'QXZlbmdlcg==' which clearly wouldn't result in a hit on 'RGFyayBBdmVuZ2Vy'.

If there IS a 1-to-1 correspondence, then EITHER your solution only makes it a bit harder to perform a SQL injection (a hacker would have to figure out what mapping was used between the text and the 'binary' format), OR you've come up with simply another way to escaping your data.  Either isn't a terribly good solution to the SQL injection problem.
dark dot avenger at email dot cz 14-Aug-2008 08:09
I think that easy way to protect against SQL injection is to convert inputted data into binary format, so that whatever input is, in sql query it will consist only of 1s and 0s.
valerylourie at gmail dot com 15-Apr-2008 07:23
Note that PHP 5 introduced filters that you can use for untrusted user input:
http://us.php.net/manual/en/intro.filter.php
ctm at etheon dot net 01-Aug-2006 03:39
This is a very helpful document from the MySQL site (in .pdf format) :

http://dev.mysql.com/tech-resources/articles/
guide-to-php-security-ch3.pdf
17-Mar-2006 05:48
If you use the PEAR package and prepare() / execute() your queries,
you will hardly have to worry about any of this. Of course, it's still
a good idea to make sure you're putting valid data in your database...

 
show source | credits | stats | sitemap | contact | advertising | mirror sites