Red de conocimiento informático - Aprendizaje de programación - ¿Son seguros los comentarios del código PHP?

¿Son seguros los comentarios del código PHP?

Artículo de seguridad de PHP Filtrado de valores de parámetros humanos ingresados ​​por el usuario

Regla 1: nunca confíes en datos o entradas externos

Lo primero que debes tener en cuenta sobre la seguridad de las aplicaciones web son los datos externos No se debe confiar. Los datos externos incluyen cualquier dato que el programador no ingrese directamente en el código PHP. No se puede confiar en los datos de cualquier otra fuente (como variables GET, formularios POST, bases de datos, archivos de configuración, variables de sesión o cookies) hasta que se tomen medidas para garantizar la seguridad.

Por ejemplo, dado que los siguientes elementos de datos están configurados en PHP, pueden considerarse seguros.

Copia el código de la siguiente manera:

lt;?php

$myUsername = 'tmyer';

$arrayUsers = array( ' tmyer', 'tom', 'tommy'); define("GREETING", 'hola' . $myUsername);?

Sin embargo, los siguientes elementos de datos son defectuosos.

Lista 2. Código inseguro y defectuoso

Copia el código de la siguiente manera:

lt;?php

$myUsername = $_POST ['nombre de usuario']; //¡contaminado!

$arrayUsers = array($myUsername, 'tom', 'tommy'); //¡manchado!

define("SALUDO", 'hola' . $myUsername); //tainted! gt;

¿Por qué la primera variable $myUsername es defectuosa? Porque viene directamente del formulario POST. Los usuarios pueden ingresar cualquier cadena en este campo de entrada, incluida la limpieza de archivos o la ejecución de comandos maliciosos desde archivos cargados previamente. Podría preguntar: "¿No se evitaría este peligro utilizando un script de validación de formularios del lado del cliente (Javascrīpt) que sólo acepte letras de la A a la Z? Sí, este siempre es un paso útil, pero como verá más adelante, cualquiera puede descargar cualquier formulario en su máquina, modificarlo y volver a enviar lo que necesiten.

La solución es simple: tienes que hacerlo en $_POST['username']. contaminar estos objetos en cualquier otro momento que use $myUsername (como en una matriz o constante).

Una manera fácil de desinfectar la entrada del usuario es usar expresiones regulares para manejar la entrada del usuario. También sería una buena idea limitar la cadena a un número específico de caracteres o exigir que todas las letras estén en minúsculas.

Lista 3. Garantizar la seguridad de la entrada del usuario

Copia el código de la siguiente manera:

lt;?php

$myUsername = cleanInput( $ _POST['nombre de usuario']); //¡limpiar!

$arrayUsers = array($myUsername, 'tom', 'tommy'); //¡limpiar!

define( " SALUDO", 'hola' .$myUsername); //¡limpiar!

función cleanInput($input){

$clean = strtolower($input);

$clean = preg_replace("/[ ^a-z]/", "", $clean = substr($clean, 0, 12);

return $clean; /p> p>

} gt;

Regla 2: deshabilite las configuraciones de PHP que dificultan la aplicación de la seguridad. Si bien sabe que no puede confiar en la entrada del usuario, también debe saber que no debe confiar en el forma en que PHP está configurado en la máquina. Por ejemplo, asegúrese de que Register_globals esté deshabilitado; si Register_globals está habilitado, es posible reemplazar accidentalmente una cadena GET o POST con el mismo nombre con una variable $. Cuando deshabilita esta configuración, PHP lo obligará a hacer referencia a las variables correctas en el espacio de nombres correcto. Para utilizar variables de un formulario POST, cite $_POST['variable']. De esta manera no confundirá una variable específica con una cookie, sesión o variable GET.

Regla 3: si no puedes entenderlo, no puedes protegerlo

Algunos desarrolladores usan una sintaxis extraña o declaraciones muy agrupadas para formar código corto pero ambiguo. Este enfoque puede ser eficaz, pero si no comprende qué hace el código, no podrá decidir cómo protegerlo.

Por ejemplo, ¿cuál de los siguientes dos fragmentos de código prefieres?

Lista 4. Haz que el código sea fácil de proteger

Copia el código de la siguiente manera:

lt;?php

//Obfuscate el código

p>

$input = (isset($_POST['username']) ?$_POST['username']: "); //código no ofuscado

$input = ";

if (isset($_POST['nombre de usuario'])){

$input = $_POST['nombre de usuario'];

}else{

$input = ";

} gt;

En el segundo fragmento de código, más claro, es fácil ver que $input es tiene fallas y debe procesarse de manera segura antes de limpiarlo.

Regla 4: "Defensa en profundidad" es la nueva magia

Este tutorial utilizará ejemplos para ilustrar cómo proteger formularios en línea. mientras que en el código PHP que maneja el formulario. Tome las medidas necesarias. Del mismo modo, incluso si usa expresiones regulares PHP para asegurarse de que las variables GET sean completamente numéricas, aún puede tomar medidas para garantizar que las consultas SQL utilicen entradas de usuario escapadas. >

La defensa en profundidad no es solo una buena idea, también es una manera de asegurarte de no meterte en problemas graves.

Ahora que hemos discutido las reglas básicas, veamos las. Primera amenaza: ataques de inyección SQL.

Prevenir ataques de inyección SQL

En un ataque de inyección SQL, el usuario agrega información a una consulta de base de datos manipulando un formulario o una cadena de consulta GET. Por ejemplo, supongamos que tiene una base de datos de inicio de sesión simple. Cada registro en esta base de datos tiene un campo de nombre de usuario y un campo de contraseña. Cree un formulario de inicio de sesión que permita a los usuarios iniciar sesión.

Lista 5. Formulario de inicio de sesión simple

Copia el código de la siguiente manera:

lt;htmlgt;

lt;headgt;

lt;titlegt;Loginlt;/titlegt;

lt;/headgt;

lt;bodygt;

lt;form action=" verificar.pgt;lt;label for='pw'gt;contraseñalt;/labelgt;

lt;enter type='contraseña' nombre='pw' id='pw'/gt;

lt; p>

lt;/pgt;

lt; plt.gt;lt; tipo de entrada='enviar' valor='iniciar sesión'/gt;lt;/pgt;

lt;/formgt;

lt;/bodygt;

lt;/htmlgt;

Este formulario acepta el nombre de usuario y contraseña ingresado por el usuario y la entrada se envía a un archivo llamado verificar.php. En este archivo, PHP procesa los datos del formulario de inicio de sesión de la siguiente manera:

Lista 6. Código de procesamiento de formulario PHP inseguro

Copia el código de la siguiente manera:

lt;?php

$okay = 0;

$username = $_POST['user'];

$pw = $_POST['pw'];

$sql = "seleccione recuento(*) como ctr de usuarios donde nombre de usuario='".$nombre de usuario "' y contraseña='".$pw."' límite 1"; $resultado=mysql_query($sql);

mientras ($datos = mysql_fetch_object($resultado)){if ($datos). - gt;ctr == 1){

//¡Pueden entrar a la aplicación!

$okay = 1;

}

}

if ($okay){

$_SESSION['loginokay'] = true;

header( "index.php");

}else{

header("login.php");

} gt;

Este código se ve bien, ¿verdad? Este código es utilizado por cientos, si no miles, de sitios web PHP/MySQL en todo el mundo. ¿Qué tiene de malo? Recuerde "no puedo confiar en la entrada del usuario". Aquí ninguna información del usuario puede escapar, por lo que la aplicación es vulnerable a ataques. Específicamente, puede ocurrir cualquier tipo de ataque de inyección SQL.

Por ejemplo, si el usuario ingresa foo como nombre de usuario y ' o '1'='1 como contraseña, la siguiente cadena se pasará a PHP, que luego pasará la consulta a MySQL:

Copia el código de la siguiente manera:

lt;?php

$sql = " select count(*) as ctr from usuarios donde nombre de usuario='foo' y contraseña=" or '1′='1′ limit 1″;? gt;

Esta consulta siempre devolverá un valor de recuento de 1, por lo que PHP permitirá el acceso. Inyectando algo de SQL malicioso en el Al final de la cadena de contraseña, un hacker puede pretender ser un usuario legítimo.

La solución a este problema es utilizar la función mysql_real_escape_string() incorporada de PHP como contenedor para cualquier entrada del usuario, que escapará. los caracteres en la cadena para que no se puedan pasar apóstrofos, etc., caracteres especiales y permite que MySQL funcione en función de caracteres especiales. El Listado 7 muestra el código de escape

Listado 7. Código de procesamiento de formularios PHP seguro<. /p>

Copia el código de la siguiente manera:

lt;?php

$okay = 0;

$username = $_POST['. usuario'];

$ pw = $_POST['contraseña'];

$sql = "seleccione recuento(*) como ctr de usuarios donde nombre de usuario='".mysql_real_escape_string($nombre de usuario). "' y contraseña='".mysql_real_escape_string($pw )."' límite 1"; $resultado = mysql_query($sql);

mientras ($datos = mysql_fetch_object($resultado)){if ( $data-gt;ctr == 1){

// ¡Pueden ingresar a la aplicación

$okay = 1;

}

if ($okay){

$_SESSION['loginokay'] = true;

header("index .php");

}else{

header("login.php");

} gt;

Utilice mysql_real_escape_string() como contenedor para que la entrada del usuario evite Realizar cualquier inyección SQL maliciosa. Si un usuario intenta pasar una contraseña con formato incorrecto mediante inyección SQL, se pasa la siguiente consulta a la base de datos:

seleccione count(*) as ctr from usuarios donde nombre de usuario='foo' y contraseña='\' o \'1 \'=\'1′ límite 1″ No hay coincidencia para dicha contraseña en la base de datos. Con solo un simple paso, se tapó un gran agujero en la aplicación web. La lección aquí es, para consultas SQL. , siempre debes escapar de la entrada del usuario

Pero todavía hay varios agujeros de seguridad que cerrar

Evitar la manipulación de las variables GET

En la sección anterior, Impedimos que los usuarios iniciaran sesión con contraseñas mal formadas.

Si es inteligente, debe aplicar lo que ha aprendido y asegurarse de que se escapen todas las entradas del usuario para las declaraciones SQL.

Sin embargo, el usuario ahora ha iniciado sesión de forma segura. Aunque el usuario tenga una contraseña válida, no significa que seguirá las reglas: existen muchas oportunidades para que las rompa. Por ejemplo, una aplicación podría permitir a los usuarios ver contenido especial. Todos los enlaces apuntan a template.php?pid=33 o template.php?pid=321. La parte de la URL después del signo de interrogación se denomina cadena de consulta. Debido a que la cadena de consulta se coloca directamente en la URL, también se denomina cadena de consulta GET.

En PHP, si Register_globals está deshabilitado, puede usar $_GET['pid'] para acceder a esta cadena. En la página template.php, puede hacer algo similar al Listado 8.

Listado 8. Ejemplo de Template.php

Copia el código de la siguiente manera:

lt;?php

$pid = $_GET [ 'pid'];

//Creamos un objeto de clase de página ficticio $ obj = new Page;

$content=$obj-gt; fetchPage($pid); /p>

/¿Ahora tenemos un montón de código PHP que muestra la página? Primero, tenemos una idea de que es seguro obtener la variable GET pid del navegador. ¿Qué está pasando aquí? La mayoría de los usuarios no son lo suficientemente inteligentes como para elaborar ataques semánticos. Sin embargo, si notan pid=33 en el campo de ubicación de la URL del navegador, podrían empezar a causar problemas. Si ingresaron otros números, entonces probablemente no haya problema, pero si ingresaron algo más, como un comando SQL o un nombre de archivo (como /etc/passwd), o hicieron algo más como ingresar un número con un valor de hasta 3000 caracteres, entonces ¿lo que sucede?

En este caso, recuerda una regla básica, no confiar en la entrada del usuario.

Los desarrolladores de aplicaciones saben que los identificadores personales (PID) aceptados por template.php deben ser numéricos, por lo que pueden garantizar que los PID no numéricos no se acepten utilizando la función is_numeric() de PHP, de la siguiente manera:

Listado 9 Utilice is_numeric() para limitar el código de copia de la variable GET. El código es el siguiente:

lt;?php

$pid = $_GET['pid'];

if (is_numeric ($pid)){

//Creamos un objeto de clase ficticia Page$obj = new Page;

$ content = $obj-gt; $pid);

// Ahora tenemos un montón de PHP}else{

// No pasa la prueba is_numeric(), ¡haz algo más!

//no pasó la prueba is_numeric(), ¡tenemos que hacer algo más! ¡Haz algo más!

} gt; work , pero is_numeric() verifica fácilmente las siguientes entradas:

100 (válido)

100.1 (no debe tener decimales)

0123.45e6 (Notación científica - mala)

0xff33669f (Hexadecimal - ¡Peligro! ¡Peligro!) Entonces, ¿qué deberían hacer los desarrolladores PHP preocupados por la seguridad? Años de experiencia han demostrado que la mejor práctica es utilizar expresiones regulares para garantizar que toda la variable GET esté formada por números, como este:

Listado 10. Utilice expresiones regulares para limitar las variables GET

Copia el código de la siguiente manera:

lt;?php

$pid = $_GET['pid'];

p>

if (strlen($pid)){

if (!ereg("^[0-9] $",$pid)){

// Haga algo apropiado, como cerrar sesión o enviarlos de regreso a la página de inicio}

}else{

//Vaciar $pid, así enviarlos de regreso a la página de inicio}

//Creamos un objeto de clase ficticia Página, que ahora está/moderadamente protegido contra entradas maliciosas del usuario $obj = new Page;

$content = $obj -gt;fetchPage ($pid);

// ¿Ahora tenemos un montón de PHP para mostrar la página? gt;

Simplemente use strlen() para verificar si la longitud de la variable es cero; de ser así, use una expresión regular totalmente numérica para garantizar que el elemento de datos sea válido. Si el PID contiene letras, barras diagonales, puntos o algo parecido a hexadecimal, esta rutina lo detectará y bloqueará la actividad del usuario en la página.

Si miras bajo el capó de la clase Page, encontrarás que los desarrolladores de PHP preocupados por la seguridad protegieron el método fetchPage() escapando del $pid ingresado por el usuario, de la siguiente manera:

Lista 11. Escapar del método fetchPage()

Copie el código de la siguiente manera:

lt;?php

clase Página{

función fetchPage($ pid){

$sql = "seleccione pid, título, desc, kw, contenido, estado de la página donde pid='".mysql_real_escape_string($pid)."'" )}

} gt;

Puede preguntar: "Dado que nos hemos asegurado de que el PID sea un número, ¿por qué necesitamos escapar de él? Porque no sabemos cuántos contextos y situaciones diferentes tiene fetchPage". () Se utilizará el método. Este método debe protegerse dondequiera que se llame, y escapar en el método encarna el significado de defensa en profundidad

Si el usuario intenta ingresar un valor muy largo, como up. a 1000 caracteres. ¿Qué sucede si lanzamos un ataque de desbordamiento del búfer? Esto se analiza con más detalle en la siguiente sección, pero por ahora podemos agregar otra verificación para asegurarnos de que el PID de entrada tenga la longitud correcta y conozcamos la longitud máxima. el campo pid de la base de datos tiene una longitud de 5 dígitos, por lo que se puede agregar la siguiente verificación.

Listado 12. Uso de expresiones regulares y comprobaciones de longitud para limitar la variable GET. >

lt;?php

$pid = $_GET['pid'];

if (strlen($pid)){

si (!ereg( "^[0-9] $", $pid) & strlen($pid) gt; 5) {//Haz algo apropiado, como cerrar sesión o enviarlos de regreso a la página de inicio}

} else {

///$pid vacío, así que envíalos de regreso a la página de inicio}

/// Creamos un objeto de clase de página ficticio, que ahora es / / más inmune a las entradas malvadas del usuario Impacto de $obj = new Page;

$content = $obj-gt;fetchPage($pid);

// Ahora tenemos un montón de PHP para mostrar la página? gt;

Hoy en día, nadie puede meter un valor de 5.000 bits en una aplicación de base de datos, al menos cuando se trata de cadenas GET. ¡Imagínese verse frustrado cuando un hacker intenta desesperadamente entrar en su aplicación! Debido a que el informe de errores está desactivado, es más difícil de detectar para los piratas informáticos.

Ataque de desbordamiento de búfer

Ataque de desbordamiento de búfer Un intento de desbordar un búfer de asignación de memoria en una aplicación PHP (o, más precisamente, en Apache o el sistema operativo subyacente). Tenga en cuenta que es posible que esté escribiendo una aplicación web en un lenguaje de alto nivel como PHP, pero en última instancia todavía esté llamando a C (en el caso de Apache). Como la mayoría de los lenguajes de bajo nivel, C tiene reglas estrictas de asignación de memoria.

Los ataques de desbordamiento de búfer envían una gran cantidad de datos al búfer, lo que hace que parte de los datos se desborden hacia los búferes de memoria adyacentes, destruyendo así el búfer o reescribiendo la lógica. Esto puede provocar una denegación de servicio, corrupción de datos o la ejecución de código malicioso en el servidor remoto.

La única forma de evitar ataques de desbordamiento del búfer es comprobar la longitud de todas las entradas del usuario.

Por ejemplo, si tiene un elemento de formulario que requiere el nombre de un usuario, agregue un atributo de longitud máxima al campo con un valor de 40 y luego verifíquelo usando substr() en el backend. El Listado 13 muestra un breve ejemplo de un formulario y código PHP.