PDA

Ver la Versión Completa : Cross Site Scripting (XSS)


dec
04-12-2006, 01:05:02
Hola a todos,

Acabo de recibir un correo de alguien que se interesó por Loturak (y venga con Loturak, qué pesado...) hasta el punto de hacer algunas pruebas... comprometodoras. El caso es que me informa de la posibilidad de conseguir realizar "Cross Site Scripting (XSS)" en la aplicación. Ha sido tan amable que me ha enviado dos URLs de ejemplo:


http://loturak.es/compartir?p=2&demo"><script>alert(123)</script>

http://loturak.es/compartir?n=20&o=fd&b=demo" onmouseover=alert(123) "



Y bien. Yo me dispongo a investigar el tema, pero, ¿se os ocurre algo? ¿qué puede estar sucediendo? ¿Cómo podría evitarse? ¿Qué demonios significa esa variable de nombre "demo"? ¿Cómo se consigue al cabo que se ejecute el JavaScript? ¿Hasta qué punto compromete la seguridad esto? Huf, huf, huf...

Gracias desde aquí a quien me envió la información. No revelo su nombre por si no le apetece. Ya le remití un correo con mi agradecimiento, entre otras cosas. Y gracias a vosotros también por cualquier comentario que tengáis a bien hacer sobre todo esto. :)

dec
04-12-2006, 01:41:28
Hola,

El problema con la primera URL solucionado... o eso creo, de momento. Se trataba de cierta función utilizada para propagar variables "GET". Precisamente, esta función no la escribí yo de mi copete, sino que más bien la adapté de cierto código, como puede verse:


/**
* Propaga las variables contenidas en $_GET en la URL de la página que
* llame a esta función. Utilizamos esta función básicamente para la pa-
* ginación de enlaces.
*
* El algoritmo principal de esta función está tomado del Script Paginator
* escrito por Jorge Pinedo Rosas (http://jpinedo.webcindario.com)
*
* @author dec
* @since 0.0.1
* @access private
* @param string $menosEstaVar Nombre de la variable que no debe propagarse
* @return string
*/
function PropagarVariblesGet($menosEstaVar = '')
{
$qs = '';
if(($menosEstaVar != '')
&& isset($_GET[$menosEstaVar]))
unset($_GET[$menosEstaVar]);

$aPropagar = array_keys($_GET);
foreach($aPropagar as $varGet)
{
if(isset($GLOBALS[$varGet])) {
$qs.= $varGet."=".$GLOBALS[$varGet]."&";
}
elseif(isset($_REQUEST[$varGet])) {
$qs.= $varGet."=".$_REQUEST[$varGet]."&";
}
} // endfor

return $qs;
}


Pues bien. Escapando las variables que nos llegan conseguimos salvar el obstáculo puesto por la primera URL. Es decir, la función ha quedado así de momento:


function PropagarVariblesGet($menosEstaVar = '')
{
global $bdatos; // Nuevo

$qs = '';
if(($menosEstaVar != '')
&& isset($_GET[$menosEstaVar]))
unset($_GET[$menosEstaVar]);

$aPropagar = array_keys($_GET);
foreach($aPropagar as $varGet)
{
$varGet = $bdatos->Escapar($varGet); // Nuevo
if(isset($GLOBALS[$varGet])) {
$qs.= $varGet."=".$GLOBALS[$varGet]."&";
}
elseif(isset($_REQUEST[$varGet])) {
$qs.= $varGet."=".$_REQUEST[$varGet]."&";
}
} // endfor

return $qs;
}


Aún estoy con la segunda de las URLs... ¿qué os parece de todo esto? ¿Curioso cuando menos no? :)

Por cierto, el método "Escapar" de la clase "Bdatos" y es tal cual:


/**
* Escapa una cadena para su inserción en la base de datos.
*
* Atención: este método no asegura la entrada del usuario, que es algo
* que deberá hacer en todo caso por otros medios.
*
* @author dec
* @since 0.0.1
* @version 1.0
* @param string $cadena Cadena a escapar.
* @return string
*/
function Escapar($cadena) {
return mysql_real_escape_string(stripslashes
(strip_tags($cadena)), $this->enlaceConexionBd);
}
/* function */

roman
04-12-2006, 01:41:43
¿Cómo lees los parámetros de entrada? Creo que sería útil pasarles un strip_tags.

// Saludos

dec
04-12-2006, 01:44:17
Hola,


¿Cómo lees los parámetros de entrada? Creo que sería útil pasarles un strip_tags.


Creo que me he adelantado. Confírmamelo Román, por favor.

Por otro lado, a ver qué ocurre con la segunda URL... :D :D :D

seoane
04-12-2006, 01:45:47
¿Cómo se consigue al cabo que se ejecute el JavaScript?


Pues insertando el script dentro del código de tu pagina.

Donde debería de aparecer esto:

<a href="/compartir?p=1" title="Mostrar la página anterior">


Pasa a aparecer esto otro

<a href="/compartir?demo"><script>alert(123)</script>=&amp;p=1" title="Mostrar la página anterior">


En tu código php tienes esto

$url = Servidor::PhpSelfSinExtension() .'?';
$url .= Servidor::PropagarVariblesGet(APP_VAR_REQUEST_NUM_PAGINA_ENLACES);
$url .= APP_VAR_REQUEST_NUM_PAGINA_ENLACES.'=';
$url = Servidor::UrlParaXhtml($url);

Como ves metes todas las variables get sin comprobar si algún listo añadió alguna por su cuenta.

Y en este otro tramo:

<a href="'.$url.$i.'" title="'._r('Mostrar la página siguiente').'">


Así que aprovecha esto para insertar parámetros de mas en la url, estos parámetros contienen caracteres como < y " que tu insertas directamente en tu pagina. De esta manera cierra la etiqueta del enlace e inserta el código del script.


¿Hasta qué punto compromete la seguridad esto?


Un script podría hacer que en tu pagina se mostraran cosas que no tienen nada que ver contigo. Con el consiguiente riesgo que esto conlleva. Por ejemplo Cross Site Scripting se utiliza para el phising.

seoane
04-12-2006, 01:46:35
:D Vale, todo mi trabajo de detective a la porra ...

dec
04-12-2006, 01:50:34
Hola,

Para nada Seoane. Ni mucho menos.

Por otro lado la segunda URL es curiosa. Se consigue el mismo efecto escribiendo en la casilla de búsqueda de Loturak: demo" onmouseover=alert(123) "

Al cabo en la página se termina escribiendo el siguiente código:


<input name="b" value="demo\" onmouseover="alert(123)" \="" id="casillabusqueda" class="inputtext" type="text">


Y el evento, lógicamente, funciona...

Y acabo de confirmar lo que me temía... funciona lo mismo con el resto de casillas de formularios... acabo de probar con el formulario para añadir enlaces y funciona... ¡ay diosito ayúdame! :D :D :D :D

dec
04-12-2006, 02:20:58
Hola,

Vale. Pues la segunda cuestión (la que plantea la segunda URL) se me está atragantando. Y es que si en el primer caso podíamos actuar en un mismo punto, en una función en concreto, en este segundo caso no parece que sea así.

He conseguido escapar la cadena de búsqueda (en que se incluye el JavaScript) con la función "htmlentities", de manera que aparentemente se soluciona el tema. Empero, he dicho antes que todas las casillas de los formularios de la aplicación están comprometidas...

Y sólo hay una función que "controle" cada una de estas casillas. El método "Escapar" que hemos visto antes. Sin embargo, aunque pareciera que esto iba a servir de ayuda, en realidad no lo parece. Véase el método "Escapar" tal y como está ahora:


function Escapar($cadena) {
return mysql_real_escape_string(stripslashes
(strip_tags($cadena)), $this->enlaceConexionBd);
}


Pues bien, si lo dejáramos tal que así:


function Escapar($cadena) {
return mysql_real_escape_string(stripslashes
(strip_tags(htmlentities($cadena))), $this->enlaceConexionBd);
}


Funcionaría por un lado, es decir, ya no tendría efecto el JavaScript de las casillas, de las variables que nos llegasen vía "HTTP GET", quiero decir, pero,... ay diosito si es que todo lo hago mal...

Resulta que el mismo método "Escapar" que usamos para las variables que nos llegan de entrada, también lo usamos cuando mostramos la información de dichas variables en las correspondientes casillas.

Me explico. Cuando vamos a mostrar la búsqueda realizada por el usuario escribimos en la casilla de búsqueda la búsqueda actual... o sea, trabalenguajes aparte, hacemos algo como esto:


$casillaBusqueda = $bdatos->Escapar(BUSQUEDA);


Pero, al hacer esto, pareciera que la función "htmlentities" codificara la cadena que se muestra en la propia casilla, de manera que esta no se muestra bien... creo que una imagen vale más que mil palabras en este caso.

Conseguimos que el JavaScript no tenga efecto, pero, los datos de las casillas se muestran malamente:

http://dec.clubdelphi.com/imagenes/captura_formulario_loturak.jpg

Total,... yo llevo creyendo hace tiempo que igual que usamos un método "Escapar" deberíamos utilizar un método "Desescapar"... o que en todo caso no estamos haciendo un buen uso del método "Escapar"... lo usamos un poco sin ton ni son, me parece a mí...

dec
04-12-2006, 02:47:55
Hola,

Bueno. Disculpad mi pesadez, pero, es que me parece curioso, la verdad. Respecto del problema con la segunda de las URLs resulta que no ocurría sólo en las casillas de los formularios... no. Cuando se realiza una búsqueda en la página Web de que hablamos se indica al usuario que los enlaces están filtrados por dicha búsqueda.

Una de las formas en que se indica es haciendo uso de diversos iconos, que tienen un "título", es decir, cuando el usuario pasa el ratón por encima le aparece el típico "hint" que le muestra algo como: "Buscando enlaces por: Delphi". Donde "Delphi" es la cadena que se está buscando.

Pues bien, lo mismo que para las etiquetas, servía para los de los títulos, así que en la medida de lo posible lo estoy corrigiendo, aunque tengo que revisar algunas cosas. Empero,... lo de las casillas... en fin. Seguiremos informando. :)

roman
04-12-2006, 07:04:57
No sé yo si diré una burrada, pero a mi me parece que no hay por qué preocuparse por este segundo tipo de "ataque". Por un lado parece más difícil de evitar porque no intervienen etiquetas html, sólo atributos, pero por otro, me da porque a quien "afecta" es sólamente al que le apetezca colocar tal texto en la caja, pero a nadie más, no al menos mientras esta información no se guarde permanetemente en algún lugar.

// Saludos

dec
04-12-2006, 07:27:55
Hola,

Qué tarea más ingrata, y, sin embargo, necesaria... lo cierto es que estos problemas no los veía venir, pero, sabía de algún modo que no estaba haciendo las cosas bien. Hablo de utilizar el método "Escapar" para todo...

Llevo desde esta tarde liado con el asunto... son las 5:20 de la mañana... estoy un poco cansado, y además hoy no era el día, no era el día y se nota... El caso es que he conseguido evitar el problema de la segunda URL.

Pero, lo he conseguido a medias aún. Es algo que tengo que seguir revisando, pues se ha visto afectado no poco código fuente de la aplicación, para mi mal. Voy a tratar de ser breve pero espero explicarme. Básicamente ya lo he dicho.

El método "Escapar", concretamente, este método:


function Escapar($cadena) {
return mysql_real_escape_string(stripslashes
(strip_tags($cadena)), $this->enlaceConexionBd);
}


Ha de utilizarse cuando tratamos con consultas en la base de datos. Véase la ayuda de la función "mysql_real_escape_string (http://es2.php.net/manual/en/function.mysql-real-escape-string.php)". Pues bien, aunque en Loturak lo estábamos usando medio bien -entre otras cosas estoy revisando esto también- lo cierto es que usábamos este mismo método para cosas como esta:


$tituloEnlace = $bdatos->Escapar($_POST['enlace-titulo']);


Y eso es correcto (según empiezo a comprender, que puede que me equivoque) si luego vamos a utilizar la variable "$tituloEnlace" dentro de una consulta SQL. Ojo, que el "Escapar" no es lo mismo que validar la entrada... esto ha de hacerse por otro lado de todas, todas, vamos, si es menester.

Pero, también estábamos usando ese método para mostrar los propios datos de los enlaces en diferentes sitios de la aplicación... es decir, por ejemplo, mostrábamos la variable "tituloEnlace" en una casilla de formulario, pero, no sólo... que no veáis qué odisea, puesto que el problema de la segunda URL, ya sabiéndolo, no se daba sólo en las casillas de los formularios... pero en muchos otros lugares...

Y aquí es donde entreba en juego otro método Escapar... pero no para cadenas a utilizar en consultas SQL, sino para usar tal como vengo diciendo ahora...


function Escapar($entrada) {
return htmlentities(strip_tags($entrada), ENT_QUOTES, 'utf-8');
}


El compañero que me informó de todo este asunto me comentó por correo electrónico que acaso sería posible "juntar" en el método "Escapar" conque contábamos antes lo de antes y el "htmlentities",... pero, aunque esto pudiera haber funcionado, lo cierto es que el compañero olvidaba mi desastrada codificación, pues, como he dicho, estaba usando el método "Escapar" para lo que no debía usarse ni era necesario...

Bien. De mi capote añado que tratándose de consultas SQL no es preciso el "htmlentities", puesto que, como digo, bastaría con escapar las cadenas con "mysql_real_escape_string", "stripslashes" y "strip_tags", como lo hacemos ahora...

En fin. No sé siquiera si llegaré a hacerme entender... la verdad, me desperté hoy con la primera en la frente... ¿recordáis el MD5 de las contraseñas? Pues después de esa taza de caldo otra más... y de qué manera... :D :D

Así que creo que voy a dejarlo por hoy, qué coño, que me lo merezco, me parece a mí y ya está bien. Ahora, como digo, aunque aparentemente solucionados los problemas que podían causar las dos URLs indicadas al inicio, comprendo que mañana, si es otro día, me queda no poco trabajo todavía con este asunto...

Bueno. Muchas gracias a todos por vuestra ayuda. Si consideráis que me equivoco en algo me lo hacéis saber con sinceridad, ¿eh? Pues eso. :)

PD. Román, ciertamente, el grado de "peligro" que pudiera darse con este tema acaso no sea de alarma roja, aunque, me da en la nariz que esto del "Cross Site Scripting" puede ir mucho más allá de mostrar una "alerta" al usuario... el mismo nombre lo puede estar diciendo... pareciera algo peligroso, lógicamente, para alguien que supiera y quisiera explotar algo así.

roman
04-12-2006, 07:39:22
PD. Román, ciertamente, el grado de "peligro" que pudiera darse con este tema acaso no sea de alarma roja, aunque, me da en la nariz que esto del "Cross Site Scripting" puede ir mucho más allá de mostrar una "alerta" al usuario... el mismo nombre lo puede estar diciendo... pareciera algo peligroso, lógicamente, para alguien que supiera y quisiera explotar algo así.

Está claro que puede ir mucho más allá de un alert. A lo que me refiero es que quien resultaría afectado sólo sería el mismo que lo puso, es decir, un suicidio.

// Saludos

dec
04-12-2006, 16:36:08
Hola,

Pues muy bien puedes estar en lo cierto Román. La verdad que es que cada día que pasa estoy más verde en todo, me parece, y en esto del Cross Site Scripting... no es que esté verde,... es que da miedo pánico, precisamente, por lo desconocido que resulta para mí.

Acabo ahora de ponerme "en marcha". Vamos a ver cómo se da el día,... que ha vuelto a empezar como ayer... con una bombilla fundida en mi habitación fruto de un cortocircuito en uno de los casquillos de la lámpara... nadie cuida de estas cosas y estas cosas se estropean con el tiempo...

Vale. Seguimos en contacto. :)

dec
04-12-2006, 17:46:09
Hola,

Voy a hacer una serie de comentarios, explicar unas cuantas ideas, para ver si alguien puede decir "no, amigo mío, en esto y aquello estás confundido". :) Se lo agradeceré enormemente, puesto que quisiera que todo esto de que ahora hablaré quedara bien claro en mi cabeza.

La clase en la que me basé para llevar a cabo la clase "BDatos" que vengo utilizando ya contaba con un método "Escapar". Yo nunca hasta hoy (si puede decirse luego se verá) he comprendido el significado de dicho método "Escapar" en la clase BDatos. Por mejor decir nunca lo comprendí en su totalidad.

Yo tenía claro que el método "Escapar" no servía para validar las entradas del usuario. Esto era tarea que había que hacer, convenientemente, por separado. Sin embargo no entendía a qué se dedicaba entonces el método "Escapar", y resulta que este método ha de utilizarse para evitar "ataques" de "SQL Injection".

Hasta hace dos días estaba usando la función "mysql_escape_string (http://es2.php.net/manual/en/function.mysql-escape-string.php)", por no mirar con detenimiento en el manual de PHP, pues resulta que esta función se considera "obsoleta" y en su lugar hay que utilizar la función "mysql_real_escape_string (http://es2.php.net/manual/en/function.mysql-real-escape-string.php)". Recordad que con esta función pretenden escaparse determinados caracteres para evitar lo que se conoce como "SQL Injection".

Esto es lo que tenía que haber tenido claro desde un principio y nunca lo tuve hasta hoy. No se trata de "escapar" caracteres para entrarlos a la base de datos o algo parecido... se trata de evitar que una consulta SQL no contenga determinados caracteres...


mysql_real_escape_string() calls MySQL's library function mysql_real_escape_string, which prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a.


Que pueden dar lugar a inyecciones SQL, como vengo diciendo, vamos, y como se dice en el manual de PHP... que yo no leí atentamente ni mucho menos.

Al confundirme yo en ese punto, es decir, al pensar que el método "Escapar" servía para descartar "caracteres peligrosos", así, en general,... y no determinados caracteres de una cadena que luego se usará en una consulta SQL... Y NADA MÁS... como yo estaba equivocado, me puse a utilizar como un loco el método "Escapar"... para casi todo.

Por ejemplo, en la entrada del usuario de un determinado formulario... ¿qué hacía? Escapar los caracteres con el método susomentado. ¿Iba a formar parte la entrada del usuario en una consulta SQL? Ah... unas veces sí, otras veces no... no tenía ningún conocimiento,... mi intención era que en la entrada del usuario no vinieran caracteres "extraños"... y ya está.

Y ya está el lío montado, quiero decir. Porque voy a tener que repasar todo el código de la aplicación, prácticamente, como buenamente pueda, en busca de estas incongruencias.

Y menos mal que al fin y al cabo conocía la diferencia entre la validación de la entrada del usuario y su "escape"... porque lo segundo puede sobrar, pero, lo primero es imprescindible y no sólo por cuestiones de seguridad, sino también porque así lo exige la propia aplicación... por ejemplo, la descripción de un enlace no ha de contener más de X caracteres y esto hay que validarlo. ¡Pero no hay que escaparlo siempre!

Hay que "escaparlo" con el método "Escapar" de la clase "BDatos" cuando dicha descripción va a formar parte de una consulta SQL... entonces sí, pero, si no es así, si únicamente va a presentarse dicha descripción en la página Web, entonces el "escape" se hace innecesario... a lo menos es "escape" que trata de evitar el "SQL Injection"... ¿Cómo iba a darse un ataque de este tipo si no se va a llevar a cabo consulta SQL ninguna?

Y aquí es donde entramos en el "Cross Site Scripting." Es decir, la cadena que vamos a validar no se incluirá en ninguna consulta SQL, pero, sí se mostrará en la página Web... código HTML... al fin y al cabo... al que se le pueden añadir "cosas" como un evento que se dispararse cuando se pasar por encima de determinado elemento. Es decir, justo lo que el compañero que me escribió amablemente para darme luz sobre todo esto propuso en la segunda URL de ejemplo que hay al comienzo de este Hilo.

Es aquí, amigos, donde entra el "Escape" necesario, que esta vez no tiene que ver con el uso de la función "mysql_real_escape_string"... sino con la función "htmlentities (http://es2.php.net/manual/en/function.htmlentities.php)", por ejemplo, junto con "strip_tags (http://es2.php.net/manual/en/function.strip-tags.php)", si se me apura, o a lo que yo entiendo. Lo que pretendemos al hacer uso de esta función es evitar las posibles etiquetas PHP y HTML (?) que se incluya en la entrada del usuario, así como convertir determinados caracteres en sus correspondientes entidades HTML.

Bien. Ya termino. Os pido disculpas por el rollo. Si habéis leído hasta aquí ya tenéis valor, ya. :D

Este es el método "Escapar" conque ahora cuenta la clase "BDatos":


function Escapar($cadena) {
// http://es.php.net/manual/en/function.mysql-real-escape-string.php
if(get_magic_quotes_gpc()) $cadena = stripslashes($cadena);
return mysql_real_escape_string($cadena, $this->enlaceConexionBd);
}


Y este es el método "Escapar" conque ahora cuenta la clase "XHtml":


function Escapar($entrada) {
return htmlentities(strip_tags($entrada), ENT_QUOTES, 'utf-8');
}


Si tenéis algo que decir sobre ellos soy todo ojos. El trabajo que me queda ahora es revisar lo necesario para que dichos métodos se utilizen donde convengan y no donde no convengan. Voy a ver si no cuesta tanto como me parece que va a costar... porque sigo estando bicho, bicho, bicho. O algo peor. :)

Bueno. No digo nada. Agur. :D

roman
04-12-2006, 18:42:02
Pues a mi me parece un estupendo resumen. Si lo sacas un poco de contexto (es decir, quitas las referencias a un proyecto en particular) me parece ideal para aparecer en la sección de trucos del Club. Estas inyecciones son algo que todos deberíamos evitar y que sin embargo muy pocos sabemos no ya como hacerlo, sino de su misma existencia.

// Saludos

dec
04-12-2006, 19:06:59
Hola,

Yo creo que me ha quedado como el culo... :) Y la verdad es que no tengo todo demasiado claro... de hecho sigo trabajando en esta cuestión... y lo que te rondaré morena. Seguiremos informando. Gracias Román. :)

roman
04-12-2006, 19:34:43
Creo que hay un problemita dec. Digamos que tu consulta es


select * from usuarios where usuario = 'pepe el toro'


Al aplicarle la función Escapar queda así:


select * from usuarios where usuario=\'pepe el toro\'


Y esto no lo acepta MySql.

// Saludos

dec
04-12-2006, 20:13:02
Hola,

No. En realidad no se le aplica "Escapar" a la consulta SQL, sino a los elementos que la conforman.

Es decir, no se hace algo como esto:


$titulo = $_POST['titulo'];
$consultaSql = Escapar("SELECT * FROM enlaces WHERE titulo = '$titulo'");


Sino que se hace algo como esto otro:


$titulo = Escapar($_POST['titulo']);
$consultaSql = Escapar("SELECT * FROM enlaces WHERE titulo = '$titulo'");


Sigo el manual de PHP, la función: mysql_real_escape_string (http://es2.php.net/manual/en/function.mysql-real-escape-string.php), concretamente el ejemplo número 2: "An example SQL Injection Attack".

dec
04-12-2006, 20:16:55
Hola,

Por cierto, el método "Escapar" de la clase XHtml va por estos derroteros por el momento... :D


function Escapar($entrada) {
return htmlentities(strip_tags
(stripslashes($entrada)), ENT_QUOTES, 'utf-8');
}

dec
05-12-2006, 01:04:36
Hola,

Bueno... pues ya estoy más tranquilo (acabo de suspirar y todo, no te digo yo que...). Menudo repaso le he pegado al asunto (léase Loturak - ¡ajá!). Lo bueno y lo malo de estas cosas es que no ves errores que los tienes ahí en tus narices... y que luego aparecen como por arte de magia. He solucionado no pocos errores o potenciales problemas (¿los llamaré "bugs"?) mientras andaba con el tema de los "escapes"...

Por otro lado creo que me ha quedado claro para siempre lo que he comentado más arriba: una cosa es el "escape" por el que tiene que pasar variable que luego tiene que pasar por una consulta SQL, y otra cosa es el "escape" que tiene que pasar una variable que luego imprimiremos en el código HTML de la página Web de turno. Si hubiera estado con más proyectos además de Loturak estoy seguro de que el problema se hubiera producido en todos ellos.


El "escape" de variables para consultas SQL

Supongamos algo como esto:



$titulo = '';
if(isset($_GET['titulo']))
$titulo = $_GET['titulo'];

$consultaSql = "SELECT * FROM enlaces WHERE titulo = '$titulo';";


Lo que entiendo ahora como correcto queda tal que así:



$titulo = '';
if(isset($_GET['titulo']))
$titulo = $bdatos->Escapar($_GET['titulo']);

$consultaSql = "SELECT * FROM enlaces WHERE titulo = '$titulo';";


Y el método "Escapar" utilizado es:



function Escapar($cadena) {
if(get_magic_quotes_gpc()) $cadena = stripslashes($cadena);
return mysql_real_escape_string($cadena, $this->enlaceConexionBd);
}


Con eso, según el manual de PHP estamos evitando un posible "ataque SQL Injection" de esos... que haberlos haylos, que me lo han dicho a mí. :D

No sigo el manual al dedillo en mi caso, puesto que según se dice en el mismo "la mejor forma de hacer esto es":


function quote_smart($value)
{
// Stripslashes
if (get_magic_quotes_gpc()) {
$value = stripslashes($value);
}
// Quote if not a number or a numeric string
if (!is_numeric($value)) {
$value = "'" . mysql_real_escape_string($value) . "'";
}
return $value;
}

Es muy probable que acabe utilizando algo así. Si no lo hago ahora es porque las consultas SQL acostumbro a realizarlas incluyendo por mi cuenta las comillas "simples" para valores de tipo "cadena" y no incluirlas para valores de tipo numérico: por lo demás, ambos métodos de escape son iguales. De hecho yo me copié del manual. :D

Nótese, sin embargo, que el método de escape no valida la entrada del usuario, es decir, se limita a escapar determinados caracteres para impedir el SQL Injection. Para validar la entrada del usuario debemos contar con las correspondientes reglas, que tendremos que aplicar por nuestra cuenta. Es decir, el código anterior quedaría algo así:




$titulo = '';
if(isset($_GET['titulo']))
$titulo = $bdatos->Escapar($_GET['titulo']);

if(!ValidarTitulo($titulo))
{
MostrarError();
die;
}
else
{
$consultaSql = "SELECT * FROM enlaces WHERE titulo = '$titulo';";
EjecutarConsulta($consultaSql);
}


Pero creo que puede verse más claro de este modo:



$titulo = '';
if(isset($_GET['titulo']))
$titulo = $bdatos->Escapar($_GET['titulo']);

if(!ValidarTitulo($titulo))
{
MostrarError();
die;
}
else
{
$consultaSql = "INSERT INTO enlaces (titulo) VALUES('$titulo');";
EjecutarConsulta($consultaSql);
}


Es decir, no aceptaremos en la base de datos enlaces que no cumplan determinadas reglas, sea que miremos por la longitud (en caracteres) del título, sea que reviremos que no contenga determinados caracteres, etc.

Así que como puede verse una cosa no excluye a la otra. Se escapan las variables que van a entrar a formar parte de una consulta SQL. Se valida la entrada del usuario siguiendo las reglas que consideremos oportunas. No son cosas iguales... aunque a mí me lo pareciera hasta anteayer. :eek: :D


El "escape" de variables que van a imprimirse en el HTML

Este tema es curioso. O a mí me lo parece, vamos. Ya visteis cómo la segunda URL que puso el compañero que informó del problema en Loturak conseguía introducir un "evento JavaScript" en un determinado elemento HTML.

Lo que más me llamó la atención (aunque no intuía aún la posible solución) es que el "problema" no se daba sólo con la URL que el compañero propuso, sino que ocurría también en todas las casillas de todos los formularios de la aplicación...

Más tarde descubrí que ni siquiera eso... el problema se daba en más sitios que en las casillas de los formularios, pero, en ambos casos estábamos hablando de lo mismo.

Cuando nos llegan variables vía GET, por ejemplo, y estas van a imprimirse en el código HTML de la página Web también hay que "escaparlas", pues, aunque ya sabemos que no estamos hablando del mismo tipo de "escape" que arriba comentamos para las consultas SQL.



function Escapar($entrada)
{
return htmlentities(strip_tags
(stripslashes($entrada)),
ENT_QUOTES, 'utf-8');
}


El "atacante" puede hacer virgerías... así que aplicamos determinados filtros a la entrada (del usuario) para tratar de "paliar" las consecuencias o eliminarlas del todo... si se puede.

Con las funciones de que se compone nuestra función "Escapar" tratamos de convertir algunos caracteres especiales de HTML a su entidad correspondiente. Por ejemplo, sustituimos los "&" por "&amp;". A continuación eliminamos de la variable "entrada" las etiquetas HTML y PHP que pudiera contener. Y para terminar eliminamos los "\" de la entrada.

Todo esto para evitar, por ejemplo, que recibamos en una variable de entrada algo como esto:


demo" onmouseover=alert(123) "

Si no escapáramos esa entrada y la misma terminara imprimiéndose tal cual en el código HTML de la página Web tendríamos algo como esto:


<input name="b" value="demo\" onmouseover=alert(123) \"" type="text" id="casillabusqueda" class="inputtext" />

Es decir, que cuando el usuario pasara el ratón por la casilla de búsqueda, en este caso, iba a recibir un pequeño susto....

¿Qué ocurre cuando se escapa la cadena de entrada con el método anteriormente visto? Algo bastante distinto, gracias, en este caso más que nada, a la función "htmlentities":


<input name="b" value="demo&quot; onmouseover=alert(123) "" id="casillabusqueda" class="inputtext" type="text">

De ese modo le mostramos al usuario lo que realmente ha buscado... pero, principalmente impedimos que el evento que trataba de introducirse surta efecto alguno.

Ahora bien. Lo curioso del asunto es que este tipo de escape no se ciñe sólo variables que nos llegan vía HTTP GET, por ejemplo. Supongamos una aplicación que permita a sus usuarios guardar datos de sus enlaces favoritos (¡ajá!).

Supongamos que alguien introduce como título de un enlace la cadena anterior. Puede que sea válida, es decir, puede que la aplicación considere que esa cadena resulta válida como título de un enlace: cumple con las reglas.

Pero, en cuanto esa cadena se introduzca como título de un enlace tendremos el problema multiplicado por la aplicación... en todo lugar en que se muestre el enlace del usuario puede terminar ocurriendo que "el evento JavScript" funcione... no sólo en las casillas de los formularios, sino en cualquier otra parte.

Precisamente, en cualquier otra parte en que se muestre el título del enlace... porque lo mismo da esto:


<input name="b" value="demo\" onmouseover=alert(123) \"" type="text" id="casillabusqueda" class="inputtext" />

Que esto otro:


<li name="b" title="demo\" onmouseover=alert(123) \"" />

El evento se añadirá esta vez en la lista en la que mostramos una serie de enlaces, por ejemplo. Como cada elemento de la lista muestra el título del enlace y el título del enlace contiene el "código malicioso"... pues eso.

Es decir, ya no recibimos la cadena "peligrosa" y a escapar desde la entrada del usuario, bueno, sí, pero, esta puede estar ya en nuestra base de datos... porque, como he dicho, la cadena anterior puede ser válida como título de un enlace y será guardada en la base de datos religiosamente... y cada vez que la precisemos mostrar en el código HTML de la aplicación tendremos problemas...

Mejor dicho tendríamos problemas. Y es que si procuramos escapar este tipo de variables con la función de más arriba, aparentemente, al menos, se acabaron las disputas en ese sentido al menos.

No es que haya que escapar todos los datos de un enlace (siguiendo con el ejemplo, para hacerme entender). Por ejemplo, si en nuestra tabla de enlaces guardamos el ID, el título y la URL de un enlace,... lo que puede resultar "peligroso" es el título y la URL, precisamente, porque es el usuario quien determina el título y la URL de los enlaces. Como el ID es un dato en el que no interviene el usuario no es necesario que lo escapemos. Y quien dice el ID dice la fecha del enlace, el número de visitas, etc., cualquier dato que el usuario no pueda editar.


Conclusiones

Dicho todo esto, se entenderá porqué tenía los problemas que tenía en la Web que me traigo entre manos: estaba utilizando el mismo método "Escapar" para todo... entradas de usuario, datos de la base de datos, variables para consultas SQL... para todo usaba el mismo método, además no del todo en condiciones... ¿cómo iba a ir la cosa? Pues mal.

Así que agradezco un montón a quien se molestó en informarme de este problema. Creo que me ha quedado lo suficientemente claro el asunto como para andarme con ojo en la aplicación de aquí en adelante y saber por dónde ando en este sentido. Muchas gracias también a todos cuantos colaborásteis de algún modo. :)

Y ya está. Eso es todo. Disculpad el rollo, y, si tenéis cualquier cosa que decir respecto a lo dicho aquí no dejéis de hacerlo, por favor. Cualquier comentario, sugerencia, crítica, correción, todo será bienvenido. :)

roman
05-12-2006, 01:21:46
Pues reitero lo dicho. Estupendo compendio. Y sobre todo, muestra que además yo estaba equivocado al pensar que podía no ser peligroso el segundo tipo de ataque que te advirtieron.

Muchas gracias por aclararnos esto a todos.

// Saludos

dec
05-12-2006, 02:15:06
Hola,

Gracias siempre a vosotros Román. Yo creo que el mismo nombre "Cross Site Scripting" quiere decir "cuidado, porque pueden en tu Script código que se ejecute en tu Script, pero que venga de fuera...". La cosa es el juego que puede dar esto. Yo creo que desde luego puede dar mucho más juego que mostrar una alerta al usuario.

Desde luego que hacer "otras cosas" no será a lo mejor tan sencillo de "pasar" que el "alert", pero, si se consigue pasar el "alert" puede que ya sea indicio suficiente como para ir más allá. Desde luego hay que tener ganas de hacerlo, saber hacerlo y ponerse a hacerlo. Yo en esto es pez.

Pero, ya digo, intuyo (por el nombre que se le da al asunto) que la cosa puede resultar: al fin y al cabo es una inyección de código, es decir, se está incluyendo código que se ejecutará en "el entorno" de tu página, acaso con ciertos privilegios, precisamente por eso, y es código que viene de fuera y puede que con malas intenciones...

Yo imagino (desde mi desconocimiento) algo así como si se consiguiera incluir un Script PHP que tu página procesase. Pienso en herramientas que ayudan a realizar el "backup" de una base de datos: este tipo de programas suelen necesitar que tú incluyas determinado código en tu Script, de manera que dicho código se procese en tu Servidor, más aún, precisamente por eso, porque si no es desde tu Servidor no pueden llevar a cabo determinadas tareas.

Pues por ahí van los tiros. ¿No? :D

warpmaster
26-12-2006, 23:15:31
Hola a todos!

Soy nuevo por estos lares. Llegué utilizando al sr. Google cuando buscaba información sobre escapar código html en comentarios y tal.

La verdad es que el hilo es muy interesante, claro y conciso. No se hasta que punto es importante estas modificaciones pero no está demás.

En mi caso, había empleado en mi web un senzillo buscador que empleaba una sentencia usando el LIKE '%xxxx' y bueno, lo que leí me pareció justificar los minutos que había que emplear en hacer ese par de cambios. ¿Se puede llegar a hacer SQL injection con un sólo campo de formulario, como en mi caso?No lo se.

En todo caso, gracias por la información!!!

Saludos