1. Code
  2. PHP

Vuelvete Limpio con PHP

La seguridad de los datos es importante y, a menudo, tanto los diseñadores como los desarrolladores y los clientes la subestiman. Desde PHP 5.2.0, la limpieza y validación de los datos se ha hecho significativamente más fácil con la introducción del filtrado de datos. Hoy, vamos a examinar más de cerca estos filtros, cómo usarlos y construir algunas funciones personalizadas.
Scroll to top
15 min read

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

La seguridad de los datos es importante y, a menudo, tanto los diseñadores como los desarrolladores y los clientes la subestiman. Desde PHP 5.2.0, la limpieza y validación de los datos se ha hecho significativamente más fácil con la introducción del filtrado de datos. Hoy, vamos a examinar más de cerca estos filtros, cómo usarlos y construir algunas funciones personalizadas.

Detalles del tutorial

  • Programa: PHP
  • Versión: 5.2.0+
  • Dificultad: principiante
  • Tiempo estimado de finalización: 20 minutos

Introducción

Siempre sentí que es fácil escribir código en PHP, y aún más fácil escribir código malo en PHP. La proliferación de PHP en la web ha sido realmente ayudada por su uso en populares paquetes de software de código abierto como WordPress, Drupal y Magento, así como en aplicaciones web importantes como Facebook; Dado que PHP se utiliza en tantos casos variados (los sitios web dinámicos, las aplicaciones web en profundidad, las plataformas de blogging, los sistemas de gestión de contenido y el comercio electrónico son solo un subconjunto de las muchas aplicaciones de PHP) las oportunidades para datos sucios y sistemas inseguros son numeroso. Este tutorial explicará algunos métodos de  Vuelvete Limpio con PHP: desinfección y validación de datos al centrarse en varias formas diferentes de entradas de datos y cómo utilizar filtros PHP y funciones personalizadas.

Sanitization and Validation of FormsSanitization and Validation of FormsSanitization and Validation of Forms

¿Por qué sanear y validar?

En este tutorial, nos centramos realmente en las entradas de datos que pueden proporcionar los usuarios o fuentes externas. Esto significa que no controlamos los datos que estamos recibiendo. Todo lo que podemos hacer es controlar lo que se hace con él después de recibirlo. Existen todo tipo de amenazas relacionadas con la seguridad de los datos provenientes de entradas de usuarios y datos de terceros.

Algunas amenazas de seguridad de datos poco populares:

  • Cross-Site Scripting (XSS): una forma de inyección de código en la que se inyecta un script en un sitio web desde un sitio web completamente diferente. Esta es, con mucho, la vulnerabilidad de seguridad más común en línea. Dos ejemplos recientes y muy destacados de esta técnica son los gusanos Stalk Daily y Mikeyy Twitter de principios de este año, que utilizaron insumos mal limpiados para lanzar Javascript a través de una interfaz web de Twitter "infectada".
  • Inyección de SQL: la segunda vulnerabilidad de seguridad en línea más común, esta es otra forma de inyección de código en la que se usa un script para participar en uno de los numerosos comportamientos de explotación que incluyen (entre otros) exponer y / o obtener acceso no autorizado a los datos, alterar datos dentro de una base de datos, o simplemente inyectar código para ser renderizado o ejecutado dentro de un sitio web, rompiendo o alterando el sitio web.
  • Falsificación de solicitudes entre sitios (CSRF / XSRF): un exploit menos común que se basa más en fuentes de datos como cookies de sesión y navegador que en entradas de datos mal saneadas y validadas, CSRF (pronunciado "sea-surf") se puede usar para ejecutar comandos un sitio web sin el permiso del usuario. Un método CSRF popular es utilizar un valor URI o src de datos de imagen con formato incorrecto para ejecutar un script en lugar de mostrar una imagen.
  • Datos incorrectos: no es realmente una "vulnerabilidad de seguridad" en sí misma, los datos incorrectos pueden causar un sinfín de problemas para el propietario de un sitio web o el administrador de la base de datos. A menudo, los datos incorrectos pueden romper sitios web mal codificados o hacer que los sistemas automatizados se bloqueen. Un ejemplo de esto fue la capacidad de alterar páginas de perfil de MySpace completas mediante la publicación utilizando todo tipo de piratería HTML / CSS (Nota: esto todavía puede funcionar; no he usado MySpace en mucho tiempo).
Exploits of a MomExploits of a MomExploits of a Mom
Fuente de la imagen: XKCD

Para nuestros propósitos, solo nos centraremos en los métodos del lado del servidor para mejorar la seguridad de los datos con PHP, así que veamos cómo se definen los términos "saneamiento" y "validación" en relación con PHP. Según el manual de PHP:

La validación se utiliza para validar o verificar si los datos cumplen con ciertos requisitos. Por ejemplo, pasar FILTER_VALIDATE_EMAIL determinará si los datos son una dirección de correo electrónico válida, pero no cambiarán los datos en sí.

La desinfección saneará los datos, por lo que puede alterarlos al eliminar los caracteres no deseados. Por ejemplo, pasar FILTER_SANITIZE_EMAIL eliminará los caracteres que no son apropiados para que contenga una dirección de correo electrónico. Dicho esto, no valida los datos.

Esencialmente, si su sitio web es el club nocturno al que todos quieren ingresar, la validación verifica la lista de invitados y las identificaciones en la puerta, mientras que el desinfectante actúa como el portero que desecha cualquier indeseable que suceda en el pasado. Con esto en mente, echemos un vistazo a la Extension PHP Filters .

¿Qué filtros tengo?

Todas las instalaciones de PHP no son iguales. Si bien PHP 5.2.0 fue la introducción de filtros, no todas las instalaciones tienen el mismo conjunto de filtros en su Extensión de filtros. La mayoría de las instalaciones tendrán todos los filtros que vamos a revisar, pero para enseñarle un poco acerca de la Extensión de Filtros, vamos a descubrir qué tiene usted en su servidor. En la descarga de origen, he incluido un archivo llamado getfilters.php que, una vez instalado y ejecutado en su servidor, mostrará todos sus filtros (ambos filtros de datos disponibles a través de la función filter_var y filtros de flujo disponibles a través de stream_filter_append).

1
2
 echo "<h1>Data Filters</h1>\n<table>\n<tr>\n";
3
 echo "<td><strong>Filter ID</strong></td>\n";
4
 echo "<td><strong>Filter Name</strong></td>\n</tr>";
5
 foreach(filter_list() as $id =>$filter) {
6
   echo "<tr><td>$filter</td><td>".filter_id($filter)."</td></tr>\n";
7
 }
8
 echo "</table>\n";

Primero, obtenemos la matriz que contiene la lista de todos los filtros disponibles con filter_list, luego hacemos un bucle a través de la matriz y hacemos eco del nombre del filtro, averiguamos la ID asignada del filtro y también hacemos eco de esta ID.

¿Cómo uso un filtro?

Los filtros PHP para validación y saneamiento se activan al pasar al menos dos valores a la función filter_var de la extensión de filtros PHP. A modo de ejemplo, usemos el filtro de desinfección para un número entero así:

1
2
$value = '123abc456def';
3
echo filter_var($value, FILTER_SANITIZE_NUMBER_INT);

En el ejemplo, tenemos una variable $value que se pasa a través de la función de extensión de filtros filter_var utilizando el filtro FILTER_SANITIZE_NUMBER_INT. Esto resulta en el siguiente resultado:

1
2
123456

El filtro de desinfección para un número entero elimina todos los caracteres no enteros de la salida y produce un entero limpio. Dentro del código fuente de descarga, puede probar varias entradas y aplicará una serie de filtros comunes a su valor de entrada. He incluido una serie de cadenas de ejemplo diferentes que también puedes probar.

¿Qué hacen los diferentes filtros?

La lista a continuación no está completa, pero contiene la mayoría de los filtros que vienen de serie con las instalaciones 5.2.0+. Los filtros personalizados y los agregados desde extensiones personalizadas no se incluyen aquí.

FILTER_VALIDATE_BOOLEAN: Comprueba si los datos pasados al filtro son o no un valor booleano de TRUE o FALSE. Si el valor es un valor no booleano, devolverá FALSE. La siguiente secuencia de comandos se haría eco de "TRUE" para los datos de ejemplo $value01, pero se haría eco de "FALSE" para los datos de ejemplo $value02:

1
2
$value01 = TRUE;
3
if(filter_var($value01,FILTER_VALIDATE_BOOLEAN)) {
4
 echo 'TRUE';
5
} else {
6
 echo 'FALSE';
7
}
8
echo '<br /><br />'
9
$value02 = TRUE;
10
if(filter_var($value02,FILTER_VALIDATE_BOOLEAN)) {
11
 echo 'TRUE';
12
} else {
13
 echo 'FALSE';
14
}

FILTER_VALIDATE_EMAIL: verifica si los datos pasados al filtro son o no una dirección de correo electrónico potencialmente válida. No comprueba si la dirección de correo electrónico existe realmente, solo que el formato de la dirección de correo electrónico es válido. La secuencia de comandos a continuación se haría eco de "TRUE" para los datos de ejemplo $value01, pero se haría eco de "FALSE" para los datos de ejemplo $value02 (porque la segunda carece de la parte @domain.tld requerida de la dirección de correo electrónico):

1
2
$value01 = 'test@example.com';
3
if(filter_var($value01,FILTER_VALIDATE_EMAIL)) {
4
 echo 'TRUE';
5
} else {
6
 echo 'FALSE';
7
}
8
echo '<br /><br />'
9
$value02 = 'nettuts';
10
if(filter_var($value02,FILTER_VALIDATE_EMAIL)) {
11
 echo 'TRUE';
12
} else {
13
 echo 'FALSE';
14
}

FILTER_VALIDATE_FLOAT: verifica si los datos pasados al filtro son o no un valor flotante válido. La secuencia de comandos a continuación se haría eco de "TRUE" para los datos de ejemplo $value01, pero se haría eco de "FALSE" para los datos de ejemplo $value02 (porque no se permiten separadores de coma en los valores flotantes):

1
2
$value01 = '1.234';
3
if(filter_var($value01,FILTER_VALIDATE_FLOAT)) {
4
 echo 'TRUE';
5
} else {
6
 echo 'FALSE';
7
}
8
echo '<br /><br />'
9
$value02 = '1,234';
10
if(filter_var($value02,FILTER_VALIDATE_FLOAT)) {
11
 echo 'TRUE';
12
} else {
13
 echo 'FALSE';
14
}

FILTER_VALIDATE_INT: verifica si los datos pasados al filtro son o no un valor entero válido. La siguiente secuencia de comandos se haría eco de "TRUE" para los datos de ejemplo $value01, pero se haría eco de "FALSE" para los datos de ejemplo $value02 (porque las fracciones / los números decimales no son enteros):

1
2
$value01 = '123456';
3
if(filter_var($value01,FILTER_VALIDATE_INT)) {
4
 echo 'TRUE';
5
} else {
6
 echo 'FALSE';
7
}
8
echo '<br /><br />'
9
$value02 = '123.456';
10
if(filter_var($value02,FILTER_VALIDATE_INT)) {
11
 echo 'TRUE';
12
} else {
13
 echo 'FALSE';
14
}

FILTER_VALIDATE_IP: verifica si los datos pasados al filtro son o no una dirección IP potencialmente válida. No comprueba si la dirección IP se resolvería, solo que se ajusta a la estructura de datos requerida para las direcciones IP. La secuencia de comandos a continuación se haría eco de "TRUE" para los datos de ejemplo $value01, pero se haría eco de "FALSE" para los datos de ejemplo $value02:

1
2
$value01 = '192.168.0.1';
3
if(filter_var($value01,FILTER_VALIDATE_IP)) {
4
 echo 'TRUE';
5
} else {
6
 echo 'FALSE';
7
}
8
echo '<br /><br />'
9
$value02 = '1.2.3.4.5.6.7.8.9';
10
if(filter_var($value02,FILTER_VALIDATE_IP)) {
11
 echo 'TRUE';
12
} else {
13
 echo 'FALSE';
14
}

FILTER_VALIDATE_URL: verifica si los datos pasados al filtro son o no una URL potencialmente válida. No comprueba si la URL se resolvería, solo que se ajusta a la estructura de datos requerida para las URL. La secuencia de comandos a continuación se haría eco de "TRUE" para los datos de ejemplo $value01, pero se haría eco de "FALSE" para los datos de ejemplo $value02:

1
2
$value01 = 'http://net.tutsplus.com';
3
if(filter_var($value01,FILTER_VALIDATE_URL)) {
4
 echo 'TRUE';
5
} else {
6
 echo 'FALSE';
7
}
8
echo '<br /><br />'
9
$value02 = 'nettuts';
10
if(filter_var($value02,FILTER_VALIDATE_URL)) {
11
 echo 'TRUE';
12
} else {
13
 echo 'FALSE';
14
}

FILTER_SANITIZE_STRING: De forma predeterminada, este filtro elimina los datos de una cadena que no es válida o no está permitida en esa cadena. Por ejemplo, esto eliminará cualquier etiqueta HTML, como <script> o <strong>  de una cadena de entrada:

1
2
$value = '<script>alert('TROUBLE HERE');</script>';
3
echo filter_var($value, FILTER_SANITIZE_STRING);

Este script eliminaría las etiquetas y devolvería lo siguiente:

1
2
alert('TROUBLE HERE');

FILTER_SANITIZE_ENCODED: muchos programadores usan la función urlencode() de PHP para manejar su codificación de URL. Este filtro esencialmente hace lo mismo. Por ejemplo, esto codificará cualquier espacio y / o caracteres especiales de una cadena de entrada:

1
2
$value = '<script>alert('TROUBLE HERE');</script>';
3
echo filter_var($value, FILTER_SANITIZE_ENCODED);

Esta secuencia de comandos codificaría la puntuación, los espacios y los corchetes, y luego devolvería lo siguiente:

1
2
%3Cscript%3Ealert%28%27TROUBLE%20HERE%27%29%3B%3C%2Fscript%3E

FILTER_SANITIZE_SPECIAL_CHARS: este filtro codificará, de manera predeterminada, caracteres especiales como comillas, ampersands y corchetes (además de los caracteres con un valor ASCII inferior a 32). Si bien la página de demostración no lo deja muy claro sin ver la fuente (porque los caracteres especiales codificados en HTML se interpretarán y representarán), si observa el código fuente, verá la codificación en funcionamiento:

1
2
$value = '<script>alert('TROUBLE HERE');</script>';
3
echo filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);

Convierte los caracteres especiales en sus seres codificados en HTML:

1
2
&#60;script&#62;alert(&#39;TROUBLE HERE&#39;);&#60;/script&#62;

FILTER_SANITIZE_EMAIL: este filtro hace exactamente lo que uno pensaría que hace. Elimina cualquier carácter que no sea válido en las direcciones de correo electrónico (como paréntesis, corchetes, dos puntos, etc.). Por ejemplo, digamos que accidentalmente agregó paréntesis alrededor de una letra de su dirección de correo electrónico (no pregunte cómo, use su imaginación):

1
2
$value = 't(e)st@example.com';
3
echo filter_var($value, FILTER_SANITIZE_EMAIL);

Elimina esos paréntesis y le devuelven su hermosa dirección de correo electrónico:

1
2
test@example.com

Este es un gran filtro para usar en formularios de correo electrónico en combinación con FILTER_VALIDATE_EMAIL para reducir el error del usuario o prevenir ataques relacionados con XSS (ya que algunos ataques anteriores de XSS implicaron la devolución de los datos originales proporcionados en un campo de correo electrónico no saneado directamente al navegador).

FILTER_SANITIZE_URL: Similar al filtro de desinfección de la dirección de correo electrónico, este filtro hace exactamente lo que uno pensaría, también. Elimina cualquier carácter que no sea válido en una URL (como ciertos caracteres UTF-8, etc.). Por ejemplo, digamos que accidentalmente agregó un "®" en la URL de su sitio web (nuevamente, no pregunte cómo, haga de cuenta que un velociraptor lo hizo):

1
2
$value = 'http://net.tuts®plus.com';
3
echo filter_var($value, FILTER_SANITIZE_URL);

Elimina el "®" no deseado y obtienes de nuevo tu hermosa URL:

1
2
http://net.tutsplus.com

FILTER_SANITIZE_NUMBER_INT: este filtro es similar al FILTER_VALIDATE_INT, pero en lugar de simplemente verificar si es un entero o no, ¡en realidad elimina todo lo que no sea entero del valor! Práctico, de hecho, para los spambots y embaucadores molestos en algunas formas de entrada:

1
2
$value01 = '123abc456def';
3
echo filter_var($value01, FILTER_SANITIZE_NUMBER_INT);
4
echo '<br />';
5
$value02 = '1.2.3.4.5.6.7.8.9';
6
echo filter_var($value02, FILTER_SANITIZE_NUMBER_INT);

Esas tonterías letras y decimales se tiran de inmediato:

1
2
123456
3
123456789

FILTER_SANITIZE_NUMBER_FLOAT: este filtro es similar al FILTER_VALIDATE_INT, pero en lugar de simplemente verificar si es un entero o no, ¡en realidad elimina todo lo que no sea un entero del valor! Práctico, de hecho, para los spambots y embaucadores molestos en algunas formas de entrada:

1
2
$value01 = '123abc456def';
3
echo filter_var($value01, FILTER_SANITIZE_NUMBER_FLOAT);
4
echo '<br />';
5
$value02 = '1.2.3.4.5.6.7.8.9';
6
echo filter_var($value02, FILTER_SANITIZE_NUMBER_FLOAT);

Una vez más, todas esas letras tontas y decimales se tiran de inmediato:

1
2
123456
3
123456789

Pero qué pasa si desea mantener un decimal como en el siguiente ejemplo:

1
2
$value = '1.23';
3
echo filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT);

Todavía lo quitaría y devolvería:

1
2
123

Una de las razones principales por las que FILTER_SANITIZE_NUMBER_FLOAT y FILTER_SANITIZE_INT son filtros separados es permitir esto a través de un indicador especial "FILTER_FLAG_ALLOW_FRACTION" que se agrega como un tercer valor pasado a filter_var:

1
2
$value = '1.23';
3
echo filter_var($value, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);

Mantendría el decimal y devolvería:

1
2
1.23

Opciones, banderas y controles de matriz, ¡OH MI!

La bandera en este último ejemplo es solo una de muchas más opciones, banderas y controles de matriz que le permiten tener un control más granular sobre qué tipos de datos se desinfectan, definiciones de delimitadores, cómo los filtros procesan las matrices y más. Puede encontrar más información sobre estas banderas y otras funciones relacionadas con el filtro en la sección Extensión de filtros del manual de PHP.

Otros Métodos de Sanidad de Datos con PHP

Ahora, repasaremos algunos métodos complementarios clave para sanear los datos con PHP para evitar que "datos sucios" causen estragos en sus sistemas. Estos son especialmente útiles para aplicaciones que aún ejecutan PHP 4, ya que estaban disponibles cuando se lanzó.

htmlspecialchars: esta función de PHP convierte 5 caracteres especiales en sus entidades HTML correspondientes:

  • '&' (ampersand) se convierte en '&amp;'
  •  '"' (comillas dobles) se convierte en '&quot;' cuando ENT_NOQUOTES no se establece.
  • ' (comilla simple) se convierte en '&#039;' solo cuando se establece ENT_QUOTES.
  • '<' (menos que) se convierte en '&lt;'
  • '>' (mayor que) se convierte en '&gt;'

Se usa como cualquier otra función de cadena PHP:

1
2
echo htmlspecialchars('$string');

htmlentities: al igual que htmlspecialchars, esta función de PHP convierte los caracteres en sus entidades HTML correspondientes. La gran diferencia es que TODOS los caracteres que se pueden convertir se convertirán. Este es un método útil para confundir las direcciones de correo electrónico de algunos bots que recopilan direcciones de correo electrónico, ya que ninguna de ellas está programada para leer htmlentities.

Se usa como cualquier otra función de cadena PHP:

1
2
echo htmlentities('$string');

mysqli_real_escape_string: esta función de MySQL ayuda a proteger contra ataques de inyección de SQL. Se considera una mejor práctica (o incluso una práctica obligatoria) pasar todos los datos que se envían a una consulta de MySQL a través de esta función. Escapa a los caracteres especiales que podrían ser problemáticos y causaría que las pequeñas mesas de Bobby destruyeran otra base de datos de otros estudiantes escolares.

1
2
$query = 'SELECT * FROM table WHERE value='.mysql_real_escape_string('$string').' LIMIT 1,1';
3
$runQuery = mysql_query($query);

Funciones personalizadas

Para muchas personas, estos filtros y funciones incorporados simplemente no son lo suficientemente buenos. La validación de datos de algunos datos como números de teléfono, códigos postales o incluso correos electrónicos a menudo requiere una validación y enmascaramiento más estrictos. Para hacer esto, muchas personas crean funciones personalizadas para validar y sus datos son reales. Un ejemplo de esto puede ser tan simple como usar una consulta de MySQL para buscar los datos en una base de datos de valores conocidos como:

1
2
function checkZipCode($value) {
3
	$zipcheck = 'SELECT COUNT(*) FROM `database`.`zipcodes` WHERE value="'.filter_var(mysql_real_escape_string($value),FILTER_SANITIZE_NUMBER_INT).'"';
4
	$count = mysql_query($zipcheck);
5
	if($count==1) {
6
		return TRUE;
7
	} else {
8
		return FALSE;
9
	}
10
}

Se pueden hacer otras funciones personalizadas que no se basan en bases de datos de valores conocidos, y se pueden crear mediante la comprobación de comillas mágicas, barras diagonales y el escape para insertarlas en una base de datos:

1
2
function cleanString($string) {
3
	$detagged = strip_tags($string);
4
	if(get_magic_quotes_gpc()) {
5
		$stripped = stripslashes($detagged);
6
		$escaped = mysql_real_escape_string($stripped);
7
	} else {
8
		$escaped = mysql_real_escape_string($detagged);
9
	}
10
	return $escaped;
11
}

Las posibilidades son infinitas, especialmente si integras expresiones regulares, pero para la mayoría de las ocasiones, la Extensión de Filtros PHP debería hacer el truco.

  • Síganos en Twitter, o suscríbase a NetTuts + RSS Feed para obtener más artículos y artículos de desarrollo web diarios.