Consideraciones de Seguridad¶
Web applications face many types of potential security problems, and it can be hard to get everything right, or even to know what «right» is in general. Flask tries to solve a few of these things by default, but there are other parts you may have to take care of yourself. Many of these solutions are tradeoffs, and will depend on each application’s specific needs and threat model. Many hosting platforms may take care of certain types of problems without the need for the Flask application to handle them.
Resource Use¶
A common category of attacks is «Denial of Service» (DoS or DDoS). This is a very broad category, and different variants target different layers in a deployed application. In general, something is done to increase how much processing time or memory is used to handle each request, to the point where there are not enough resources to handle legitimate requests.
Flask provides a few configuration options to handle resource use. They can also be set on individual requests to customize only that request. The documentation for each goes into more detail.
MAX_CONTENT_LENGTH
orRequest.max_content_length
controls how much data will be read from a request. It is not set by default, although it will still block truly unlimited streams unless the WSGI server indicates support.MAX_FORM_MEMORY_SIZE
orRequest.max_form_memory_size
controls how large any non-filemultipart/form-data
field can be. It is set to 500kB by default.MAX_FORM_PARTS
orRequest.max_form_parts
controls how manymultipart/form-data
fields can be parsed. It is set to 1000 by default. Combined with the defaultmax_form_memory_size
, this means that a form will occupy at most 500MB of memory.
Regardless of these settings, you should also review what settings are available from your operating system, container deployment (Docker etc), WSGI server, HTTP server, and hosting platform. They typically have ways to set process resource limits, timeouts, and other checks regardless of how Flask is configured.
Cross-Site Scripting (XSS)¶
Cross site scripting es el concepto de inyectar HTML arbitrario (y con él JavaScript) en el contexto de un sitio web. Para remediar esto, los desarrolladores deben escapar el texto correctamente para que no pueda incluir etiquetas HTML arbitrarias. Para obtener más información al respecto, consulta el artículo de Wikipedia sobre Cross-Site Scripting.
Flask configura Jinja2 para escapar automáticamente todos los valores a menos que se indique explícitamente lo contrario. Esto debería descartar todos los problemas XSS causados en las plantillas, pero todavía hay otros lugares en los que debes tener cuidado:
generando HTML sin la ayuda de Jinja2
llamando a
Markup
en los datos enviados por los usuariosenviar HTML desde archivos cargados, nunca hagas eso, usa el encabezado
Content-Disposition: adjunto
para evitar ese problema.enviar archivos de texto a partir de archivos cargados. Algunos navegadores estan utilizando adivinar el tipo de contenido basándose en los primeros bytes para que los usuarios puedan engañar a un navegador para que ejecute HTML.
Otra cosa que es muy importante son los atributos sin comillas. Si bien Jinja2 puede protegerte de problemas XSS al escapar HTML, hay una cosa de la que no puede protegerte: XSS mediante inyección de atributos. Para contrarrestar este posible vector de ataque, asegúrate de poner tus atributos entre comillas dobles o simples cuando utilices expresiones Jinja en ellos:
<input value="{{ value }}">
¿Por qué es esto necesario? Porque si no lo hiciera, un atacante podría inyectar fácilmente controladores de JavaScript personalizados. Por ejemplo, un atacante podría inyectar este fragmento de HTML+JavaScript:
onmouseover=alert(document.cookie)
Cuando el usuario pase el mouse sobre la entrada, la cookie se presentaría al usuario en una ventana de alerta. Pero en lugar de mostrar la cookie al usuario, un buen atacante también podría ejecutar cualquier otro código JavaScript. En combinación con inyecciones de CSS, el atacante podría incluso hacer que el elemento llene toda la página, de modo que el usuario solo tuviera que colocar el mouse en cualquier lugar de la página para desencadenar el ataque.
Hay una clase de problemas de XSS contra los cuales el entrecomillado de Jinja no protege. El atributo href
de la etiqueta a
puede contener un URI ``javascript:`, que el navegador ejecutará al hacer clic si no está protegido adecuadamente.
<a href="{{ value }}">click here</a>
<a href="javascript:alert('unsafe');">click here</a>
Para evitar esto, deberás configurar la Política de seguridad de contenido (CSP) del encabezado de respuesta.
Cross-Site Request Forgery (CSRF)¶
Otro gran problema es CSRF. Este es un tema muy complejo y no lo describiré aquí en detalle, solo mencionaré qué es y cómo prevenirlo teóricamente.
Si su información de autenticación se almacena en cookies, tienes gestión de estado implícita. El estado de «haber iniciado sesión» está controlado por una cookie, y esa cookie se envía con cada solicitud a una página. Desafortunadamente, eso incluye solicitudes lanzadas por sitios de terceros. Si no tienes esto en cuenta, algunas personas podrían engañar a los usuarios de tu aplicación con ingeniería social para que hagan cosas indebidas sin que ellos lo sepan.
Supongamos que tiene una URL específica que, cuando envía solicitudes POST
elimina el perfil de un usuario (digamos http://example.com/user/delete
). Si un atacante ahora crea una página que envía una solicitud de publicación a esa página con algo de JavaScript, solo tiene que engañar a algunos usuarios para que carguen esa página y sus perfiles terminarán siendo eliminados.
Imagina que tuvieras que administrar Facebook con millones de usuarios simultáneos y alguien enviara enlaces a imágenes de pequeños gatitos. Cuando los usuarios iban a esa página, sus perfiles se eliminaban mientras miraban imágenes de gatos peludos.
¿Cómo puedes prevenir eso? Básicamente, para cada solicitud que modifique el contenido en el servidor, tendría que usar un token único y almacenarlo en la cookie y también transmitirlo con los datos del formulario. Después de recibir los datos en el servidor nuevamente, deberá comparar los dos tokens y asegurarse de que sean iguales.
¿Por qué Flask no hace eso por ti? El lugar ideal para que esto suceda es el marco de validación de formularios, que no existe en Flask.
Seguridad JSON¶
En Flask 0.10 y versiones anteriores, jsonify()
no serializaba matrices de nivel superior a JSON. Esto se debió a una vulnerabilidad de seguridad en ECMAScript 4.
ECMAScript 5 cerro esta vulnerabilidad, por lo que solo navegadores extremadamente viejos son los unicos que tienen esta vulnerabilidad. Todos estos navegadores tienen otras vulnerabilidades mas serias, por lo que este comportamiento ha sido cambiado y jsonify()
ahora soporta arreglos serializados.
Cabeceras de Seguridad¶
Los navegadores reconocen varios encabezados de respuesta para controlar la seguridad. Recomendamos revisar cada uno de los encabezados a continuación para usarlos en tú aplicación. La extensión Flask-Talisman se puede utilizar para administrar HTTPS y los encabezados de seguridad por ti.
HTTP Strict Transport Security (HSTS)¶
Le indica al navegador que convierta todas las solicitudes HTTP a HTTPS, previniendo ataques de intermediario (MITM).
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
Política de seguridad de contenido (CSP)¶
Indica al navegador desde dónde puede cargar distintos tipos de recursos. Esta cabecera debe utilizarse siempre que sea posible, pero requiere cierto trabajo para definir la política correcta para tú sitio. Una política muy estricta sería:
response.headers['Content-Security-Policy'] = "default-src 'self'"
X-Content-Type-Options¶
Obliga al navegador a respetar el tipo de contenido de respuesta en lugar de intentar detectarlo, lo que puede utilizarse de forma abusiva para generar un ataque cross-site scripting (XSS).
response.headers['X-Content-Type-Options'] = 'nosniff'
X-Frame-Options¶
Evita que sitios externos incrusten tú sitio en un iframe
. Esto evita una clase de ataques en los que los clics en el marco externo pueden traducirse de forma invisible en clics en los elementos de tú página. Esto también se conoce como «clickjacking».
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
HTTP Public Key Pinning (HPKP)¶
Esto le indica al navegador que se autentique en el servidor utilizando solo la clave de certificado específica para prevenir ataques MITM.
Advertencia
Ten cuidado al activar esto, ya que es muy difícil de deshacer si configura o actualiza su clave de forma incorrecta.
Copiar/Pegar a la Terminal¶
Los caracteres ocultos como es la barra invertida (\b
, ^H
) pueden hacer que el texto se represente de manera diferente en HTML que cómo se interpreta si se pega en una terminal.
Por ejemplo, import y\bose\bm\bi\bt\beb
se representa como import yosemite
en HTML, pero los espacios se aplican cuando se pega en un terminal, y se convierte en import os
.
Si esperas que los usuarios copien y peguen código no fiable de su sitio, como el de los comentarios publicados por los usuarios en un blog técnico, considere la posibilidad de aplicar un filtrado adicional, reemplazando todos los caracteres \b
.
body = body.replace("\b", "")
La mayoría de los terminales modernos advierten y eliminan los caracteres ocultos al pegar, por lo que esto no es estrictamente necesario. También es posible crear comandos peligrosos de otras formas que no son posibles de filtrar. Dependiendo del caso de uso de tu sitio, puede ser bueno mostrar una advertencia sobre la copia de código en general.