¿Por qué asegurar tu sitio?
En la actualidad, el Internet es robusto y se extiende taaaanto, hay personas que quieren fastidiar y son maliciosas. Eso resume bastante bien por qué deberías asegurar todo. La seguridad de PHP ya no es una opción; es una necesidad. Los sitios son hackeados a diario y a medida que construyes un sitio usando PHP, debes saber cómo mantenerlo a salvo de los malos.
¿Qué es la seguridad de PHP?
La seguridad de PHP, como su nombre lo indica, está asegurando tu sitio en PHP para ayudar a evitar que los delincuentes obtengan acceso no autorizado a los datos de tu sitio. Te ayuda a mantener la integridad de tus datos y garantiza su disponibilidad según sea necesario. Puedes comenzar a hacer esto en PHP validando y desinfectando los datos en tu página web, que es lo que compartiremos en este artículo.
Dado que esta es una publicación para principiantes sobre validación básica y desinfección, quieres obtener más información sobre cómo mantener un sitio seguro. Como dice el Maestro Yoda: “Mucho que aprender, todavía tienes”.
Validación de información de usuario y algo sobre desinfectar.
Validar la información de usuario es el primer y más importante paso para asegurar tu sitio. Validar significa verificar que los datos que ingresan a tu script son el tipo de datos que deseas, están en el formato correcto y tienen la longitud correcta. Sin verificar esto, tu sitio es vulnerable.
Dependiendo de lo que haga tu script, puedes hacer que el sitio se caiga, muestre información incorrecta, otorgue a los malos acceso a obtener información de los usuarios y mucho más.
Conoce los datos entrantes
El primer paso para validar tus datos es saber qué datos deben ingresar. Si alguien está tratando de hackear tu sitio, pueden ingresar datos adicionales. Y sí estás aceptando cualquier información que ingrese, entonces es vulnerable porque permitimos que las personas hagan lo que quieran.
Imagina que tienes un formulario de usuario que acepta agregar comentarios en una página. Tienes campos para que alguien agregue un comentario que incluya su nombre, dirección de correo electrónico, comentario y un campo oculto del ID de la página que está comentando. Cuando el usuario envía un comentario, un script procesa el comentario y lo agrega a una base de datos.
Ahora que tenemos una idea de qué información llegará a nuestro script, debemos verificar que tenemos los datos correctos, el tipo de datos, un límite en la longitud de los datos y que no estemos usando nada más allá de los datos que necesitamos.
Dado que este formulario de comentarios se enviará a nuestro script como una variable POST, no queremos recorrer cada campo del POST sin saber qué es lo que queremos. Aquí hay un ejemplo de una variable POST que se envía a nuestro script:
Array
(
[name] => Jerry
[email] =>jerryw@fake.dreamhost.com
[comment] => This is a test comment that is coming to our site
[submit] => Post Comment
[page_ID] => 37
)
Esto muestra que tenemos exactamente los datos que solicitamos, pero si un hacker informático desea agregar información adicional (como un campo extra), entonces podría haber posibilidades de corromper tu sitio. Para un formulario como este, recomiendo nombrar a cada campo, para que sepas que solo está utilizando lo que necesita tu script. Por ejemplo, en lugar de recorrer $_POST, puedes nombrar a cada campo de esta manera:
$_POST[ ‘name’ ]
$_POST[ ‘email’ ]
Esto ayudará a aceptar sólo los datos que estás esperando e ignorar el resto.
A continuación, debes saber cuáles se supone que son los datos. Por ejemplo, el $_POST[ ‘page_ID’ ] va a ser un número entero, porque es solo una identificación de página que es un número. Entonces, sabemos que no queremos aceptar caracteres o letras especiales para esto. Sabemos que $_POST[ ‘email’ ] es una dirección de correo electrónico, por lo que queremos verificar el formato para asegurarnos de que sea una dirección de correo electrónico válida. En este ejemplo, pondremos que no queremos permitir comentarios de más de 256 caracteres.
¿Te preguntas cómo DreamHost mantiene seguro tu sitio web? Echa un vistazo a estas preguntas y respuestas con nuestro Director de Tecnología.
Verificación del tipo de datos y limpieza
Ahora que sabemos qué datos estamos aceptando, y sabemos lo que está permitido ser, verifiquemos el tipo de datos que ingresan.
La mayoría de los datos que provienen de una publicación se consideran una cadena. A veces puedes tener campos como moneda entrante o una ID de página (como en este ejemplo), que sabemos que solo se supone que es un número.
Primero, cuando recibimos datos, queremos verificar si los datos que necesitamos están allí. Entonces, queremos verificar si realmente tiene algo allí. Aquí hay una manera de verificar si un campo realmente llegó.
if ( isset( $_POST[ ‘name’ ] ) )
$name = strip_tags( trim( $_POST[ ‘name’ ] ) );
Aquí verificamos si el nombre está allí con la función isset(). Esto confirma si la variable está allí y también verifica que la variable no sea NULL. También introduje otras dos funciones strip_tag() y trim(). La función strip_tags() elimina todas las etiquetas HTML y PHP de una variable. Como sabemos que ese nombre es solo el nombre de una persona y no necesita enlaces, o posiblemente código malicioso, no necesitamos ninguna etiqueta. Entonces, si una persona agregara <a href=”http://www.google.com”>Jerry</a>, solo permitiría asignar la cadena “Jerry” a la variable. La función trim() simplemente elimina cualquier espacio en blanco desde el principio y el final de la cadena (Nota: si observas esta función en el sitio web de PHP, puedes obtener información sobre otros caracteres que puedes eliminar con esta función. Para esta publicación, sin embargo, solo estamos quitando el espacio en blanco).
A continuación, verificaremos el tipo de ID de nuestra página. Técnicamente, esto se puede hacer de dos maneras (hay algunas otras que no revisaré en esta publicación). Primero, podemos probar si el ID de la página es un número entero usando esto:
if ( is_int( $_POST[ ‘page_ID’ ] ) )
$pageID = $_POST[ ‘page_ID’ ];
Esto utiliza la función is_int() de PHP para probar si $_POST[ ‘page_id’ ] es en realidad un número entero. Si es así, entonces asigna la variable a $pageID. Hay funciones similares que puedes usar, is_bool(), is_float(), is_numberic() y algunas otras.
La otra forma de hacer esto es asignar $_POST[ ‘page_ID’ ] a la variable usando el forzado de tipos.
Aquí hay un ejemplo:
$pageID = (int) $_POST[ ‘page_ID’ ];
El uso de (int) obliga al page_ID a ser un número entero. Entonces, si el valor que ingresas es una cadena, en lugar de un número entero, lo forzará a ser un número entero o cero (0) si no es un número entero. Luego, podrías probar si el valor es igual a 0 y devolver un error si lo es.
Ahora, echemos un vistazo a la sección de comentarios. La sección de comentarios podrá agregar etiquetas, en caso de que alguien quiera agregar un enlace, por lo que no queremos usar la función strip_tags(), ya que esto eliminaría su etiqueta <a>. Para lograr esto, utilizaremos la función htmlentities(). Esta función convierte caracteres en entidades HTML. Por ejemplo, el carácter ‘<’ se traduciría a ‘& lt;’. Aquí hay un ejemplo de cómo hacemos esto para la sección de comentarios:
if ( isset( $_POST[ ‘comment’ ] ) )
$comment = htmlentities ( trim ( $_POST[ ‘comment’ ] ) , ENT_NOQUOTES );
Aquí, verificamos si el campo de comentarios llegó, y si lo hizo, lo asignamos a la variable $comment usando htmlentities(). Por lo tanto, si se incluyen etiquetas, estas se convertirán. Digamos que alguien agrega el enlace:
<a href=”https://www.dreamhost.com/”>Awesome Hosting</a>
Después de que pasa por la función htmlentities() anterior, será esto:
<a href=”https://www.dreamhost.com/”>Awesome Hosting</a>
Esto está utilizando la opción ENT_NOQUOTES. Si echas un vistazo a esta función en el sitio web de PHP, hay otras opciones, dependiendo de lo que quieras hacer.
Comprobando la longitud de las variables
Esto puede no parecer crítico, pero verificar la longitud de las variables es bastante importante. Sin verificar las variables, un usuario podría causar problemas de desbordamiento del búfer. No solo eso, sino que si tienes una tabla en tu base de datos con el nombre como comentario, y solo puedes tener 256 caracteres. Si un usuario escribe 356 caracteres, parte de su publicación se cortará. Si verificas la longitud, puedes informar al usuario que necesita acortar su comentario.
Para verificar la longitud de una cadena, usa la función strlen(). Esta función te devuelve la longitud de una cadena. Aquí hay un ejemplo:
if ( strlen( $_POST[ ‘comment’ ] ) <= 256 )
$comment = htmlentities ( trim ( $_POST[ ‘comment’ ] ) , ENT_NOQUOTES );
Aquí, verificamos si la longitud de $_POST[ ‘comment’ ] es más corta o igual a 256. Si es así, la asignamos a la variable. Otra opción para verificar que la longitud de la cadena es suficiente para ser un comentario es algo como esto:
if ( strlen( $_POST[ ‘comment’] ) >= 1 && strlen( $_POST[ ‘comment’ ] ) <= 256 )
$comment = htmlentities ( trim ( $_POST[ ‘comment’ ] ) , ENT_NOQUOTES );
Esto verifica que la longitud del comentario sea más de un carácter.
¿Es el formato correcto desde usuario?
Asegurarse de que el formato sea apropiado es importante para verificar que la información pueda usarse correctamente más adelante, pero también para el control de errores en el sitio. En esta publicación, usaremos la función PHP preg_match() con expresiones regulares para lograr esto.
Antes de entrar en los comandos reales que usaremos, quería mencionar que no explicaré las expresiones regulares en esta publicación. Si quieres obtener más información, hay tutoriales en línea. Para esta publicación, tomaremos nuestras expresiones regulares de http://regexlib.com, que tiene bastantes expresiones regulares que puedes usar.
La función preg_match() busca, en una variable, un patrón de expresión regular para ver si coincide. Por ejemplo, verifiquemos si nuestra dirección de correo electrónico es válida:
if ( preg_match( ‘/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/’, $_POST[ ’email’ ] ) )
$emailAddress = trim( $_POST[ ’email’ ] );
Esto toma una expresión regular, que en este caso es ‘^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$’ , y comprueba el $_POST[ ‘email’ ] para verificar que coincida con ese patrón. Según el sitio regexlib.com, coincidirá con los siguientes formatos:
joe@aol.com | joe@wrox.co.uk | joe@domain.info
Si el usuario no usa uno de los formatos anteriores, devolverá falso, y la variable $emailAddress no recibirá la dirección de correo electrónico asignada. preg_match() permite verificar el formato de cualquier variable, siempre que la expresión regular sea correcta. Para usarlo, puedes hacer lo anterior:
if ( preg_match( ‘/<ENTER EXPRESSION HERE>/’, <INSERT VARIABLE HERE> ) )
Cuando lo uses de esta forma, asegúrate de agregar las barras diagonales delante y detrás de la expresión regular como se muestra arriba.
Las expresiones regulares son bastante poderosas y se pueden usar para probar muchos patrones diferentes. Puedes probar todo, desde números de teléfono hasta los tipos de caracteres. Echa un vistazo a http://regexlib.com para ver más patrones que puedes usar.
Desinfectar tus datos: Un poco más de información
La desinfección de datos es otro elemento esencial de la seguridad de PHP. En nuestra última sección, validando la entrada del usuario y algo de desinfección, desinfectamos un poco como parte de la limpieza. Validamos nuestros datos verificando si coincidían con los datos que queríamos. Aquí hay dos consejos más para ayudar a proteger tu sitio de los malos.
Inyección MySQL
¿Qué es la inyección de MySQL? Básicamente, es cuando los malos intentan manipular tu sitio para agregar SQL a tu comando SQL para obtener más información, modificar o eliminar cosas de tu base de datos. Aquí hay un ejemplo de una inyección SQL simple:
$userID = $_POST[ ‘user_id’ ]; // This is a value of “‘ OR 1′”;
$query = “SELECT * FROM users WHERE user_id = ‘$userID’”;
//output: SELECT * FROM users WHERE user_id = ” OR 1”
Este ejemplo muestra un script que no se ha protegido, por lo que el creador del script ingresó el $_POST[ ‘user_id’ ] directamente en el SQL del sitio. Algún Hacker vino y decidió cambiar el valor en la forma oculta de un número a “OR 1”. Ahora, si esto se usó para consultar a un usuario, en realidad sacaría a todos los usuarios de la tabla porque cuando cambias el script a WHERE user_id = ‘’ OR 1, extraerá todas las filas de la tabla.
Wow, entonces, ¿cómo detenemos este truco? Afortunadamente, esta es una guía para principiantes, ¡así que tenemos el método perfecto para ti!
PHP tiene una función llamada mysql_real_escape_string() que ayuda a prevenir la inyección. Antes de usar esta función, debes validar todos los datos y desinfectarlos para asegurarte de que estén limpios. Supongamos que validamos todos los datos para nuestro formulario de comentarios y ahora queremos agregarlos a la base de datos. Pero también digamos que soy un tipo malo y trató de inyectar algunas cosas secretas en tu sitio maliciosamente. Por lo tanto, en realidad él puso page_ID como ‘OR 1’, como lo mencionamos anteriormente, y te olvidó de desinfectar el page_ID. (Sé que realmente no te olvidarías de hacer eso)
Cómo utilizamos nuestra función mysql_real_escape_string(), evitamos la inyección. Aquí hay un ejemplo:
$pageID = mysql_real_escape_string( $_POST[ ‘page_ID’ ] ); // This is a value of “‘ OR 1′”;
$query = “SELECT * FROM pages WHERE page_id = ‘$pageID’”;
//output: SELECT * FROM pages WHERE page_id = ‘\’ OR 1 \”
Como puedes ver en el resultado de esto, el ‘Or 1’ en realidad se convirtió en \’ OR 1 \.}, lo que impidió la modificación del dónde (WHERE), lo que detuvo que salieran datos adicionales. Nuevamente, este es un primer paso para detener la inyección y sugiero leer más sobre cómo prevenir esto. PHP tiene otros métodos para acceder a una base de datos.
¡Los planes DreamPress Plus y Pro ahora incluyen Jetpack Premium y sus increíbles ventajas de seguridad de WordPress!
Solo un poco de inyección cruzada
Como ya hablamos mucho, quería escribir un poco más sobre la inyección cruzada o Cross-Site. Es cuando los delincuentes inyectan datos en tu sitio, que luego se enviarán al lado del cliente final, para obtener datos de los usuarios de manera malintencionada, modificar tu sitio para cambiar o eliminar datos. La inyección entre sitios es una gran vulnerabilidad de seguridad. Entonces, ¿cómo puedes ayudar a evitar que esto suceda?
Bueno, primero puedes usar esa función htmlentities() de confianza que usamos anteriormente. El uso de esto garantiza que cualquier dato que haga eco sea más seguro, por lo que los molestos hackers no podrán inyectarse en tu sitio. Por ejemplo, supongamos que un usuario visita tu sitio, comenta en tu página y agrega lo siguiente como su código:
<iframe src=”http://bad-dude-hacker-mafia.com/xss-injection.php” height=0 width=0 />
Si no hicimos nada para proteger nuestro sitio, y esto se mostraría en la página cada vez que alguien lo viera, podrían recopilar datos, mostrar información en el sitio, etc. Pero, si usamos nuestra función htmlentities(), podemos evitar esto:
echo htmlentities ( trim ( $comment ) , ENT_NOQUOTES );
//Output: <iframe src=”http://bad-dude-hacker-mafia.com/xss-injection.php” height=0 width=0 />
Como puedes ver en el resultado, esto puede mostrarse como texto, pero en realidad no abrirá el sitio bad-dude-hacker-mafia.com y no se causarían estragos.
Yoda para el camino
¡Bueno! Ahora has aprendido cómo proteger tu sitio PHP utilizando validación, desinfección, prevención de inyección MySQL y algunas habilidades de inyección entre sitios. Recuerda, esto es solo el comienzo. Hay mucha información en línea para ayudar a proteger tu sitio, y cuanto más sepas, más seguro estarás.
Transmite lo que has aprendido, Luke. Hay otro Skywalker.
¡Sí, Yoda es muy sabio!
¿Tienes problemas con otro proveedor de hosting web? ¡Cámbiate al galardonado hosting de DreamHost!