25 de septiembre de 2019

No utilizar el número IMEI como identificador de usuario en aplicaciones móviles

Es una indudable ventaja el hecho de poder obtener el número IMEI de un dispositivo móvil desde una aplicación para cualquiera de las plataformas móviles de la actualidad, sin embargo utilizarlo como identificador único para acceder a un servicio web puede ser un error bastante costoso.

La realidad es que si bien el Numero IMEI (International Mobile Equipment Identity)
es un número único e irrepetible pregrabado en todos los equipos GSM que lo identifica a nivel mundial, es muy tentador asociar ese número a un usuario determinado, por lo que empieza a ser una práctica convencional.

Sin embargo, el paso de este número específico como parámetro de identificación a un servicio web o rutina, inclusive a través de una conexión por socket sin utilizar cifrado es una práctica muy riesgosa, por una parte debido a que dicho número es de fácil obtención por parte de cualquier persona con acceso al dispositivo, y por otra porque la intercepción de dicho número por parte del atacante puede implicar serios riesgos en sistemas de identificación que lo usen.

Empecemos con un simple ejemplo que funciona en absolutamente todos los teléfonos GSM. Simplemente pulsemos la secuencia de caracteres *#06# en el teclado de un dispositivo e inmediatamente obtendremos una ventana en la que aparece nuestro número IMEI.

Pero eso no es todo, el número IMEI además en su composición guarda información específica acerca del dispositivo, que al cotejarse en una base de datos puede revelar demasiados datos acerca de nuestro teléfono. Para comprobarlo, obtengan su número IMEI mediante la secuencia de teclado que explicamos en el párrafo anterior e introduzca el mismo en este sitio: http://www.imei.info/ . Obtentrán una pantalla informativa impresionante como la que les muestro a continuación:



Definitivamente asociar un usuario a un teléfono móvil puede ser extremadamente útil en aplicaciones de seguridad de todo tipo entre ellas aplicaciones para realizar transacciones desde el teléfono o para obtener acceso. La mayoría de los sistemas operativos móviles permiten acceder a números únicos de identificación del dispositivo derivados del IMEI o de algún otro número como por ejemplo la identificación del dispositivo de red wireless. Si aún así decides usar el IMEI, entonces no lo pases jamás como parámetro sin combinar y cifrar.




OWASP TOP 10 2017

Nuevamente OWASP nos hace llegar la lista de las más importantes vulnerabilidades en lo referente a desarrollo web en su lista del 2017 Top 10.

OWASP Top 10 2017

Riesgos en Seguridad de Aplicaciones

A1:2017 Inyección

Las fallas de inyección, como SQL, NoSQL, OS o LDAP ocurren cuando se envían datos no
confiables a un intérprete, como parte de un comando o consulta. Los datos dañinos del atacante
pueden engañar al intérprete para que ejecute comandos involuntarios o acceda a los datos sin
la debida autorización.


A2:2017 Pérdida de Autenticación

Las funciones de la aplicación relacionadas a autenticación y gestión de sesiones son
implementadas incorrectamente, permitiendo a los atacantes comprometer usuarios y
contraseñas, token de sesiones, o explotar otras fallas de implementación para asumir la
identidad de otros usuarios (temporal o permanentemente).



A3:2017 Exposición de datos sensible

Muchas aplicaciones web y APIs no protegen adecuadamente datos sensibles, tales como
información financiera, de salud o Información Personalmente Identificable (PII). Los atacantes
pueden robar o modificar estos datos protegidos inadecuadamente para llevar a cabo fraudes
con tarjetas de crédito, robos de identidad u otros delitos. Los datos sensibles requieren métodos
de protección adicionales, como el cifrado en almacenamiento y tránsito.



A4:2017 Entidades Externas XML (XXE)

Muchos procesadores XML antiguos o mal configurados evalúan referencias a entidades
externas en documentos XML. Las entidades externas pueden utilizarse para revelar archivos
internos mediante la URI o archivos internos en servidores no actualizados, escanear puertos de
la LAN, ejecutar código de forma remota y realizar ataques de denegación de servicio (DoS).



A5:2017 Pérdida de Control de Acceso

Las restricciones sobre lo que los usuarios autenticados pueden hacer no se aplican
correctamente. Los atacantes pueden explotar estos defectos para acceder, de forma no
autorizada, a funcionalidades y/o datos, cuentas de otros usuarios, ver archivos sensibles,
modificar datos, cambiar derechos de acceso y permisos, etc.



A6:2017 Configuración de Seguridad Incorrecta

La configuración de seguridad incorrecta es un problema muy común y se debe en parte a
establecer la configuración de forma manual,
ad hoc o por omisión (o directamente por la falta de
configuración). Son ejemplos:
S3 buckets abiertos, cabeceras HTTP mal configuradas, mensajes
de error con contenido sensible, falta de parches y actualizaciones,
frameworks, dependencias y
componentes desactualizados, etc.



A7:2017 Secuencia de Comandos en Sitios Cruzados (XSS)

Los XSS ocurren cuando una aplicación toma datos no confiables y los envía al navegador web
sin una validación y codificación apropiada; o actualiza una página web existente con datos
suministrados por el usuario utilizando una API que ejecuta
JavaScript en el navegador. Permiten
ejecutar comandos en el navegador de la víctima y el atacante puede secuestrar una sesión,
modificar (
defacement) los sitios web, o redireccionar al usuario hacia un sitio malicioso.

A8:2017 Deserialización Insegura

Estos defectos ocurren cuando una aplicación recibe objetos serializados dañinos y estos objetos
pueden ser manipulados o borrados por el atacante para realizar ataques de repetición,
inyecciones o elevar sus privilegios de ejecución. En el peor de los casos, la deserialización
insegura puede conducir a la ejecución remota de código en el servidor.



A9:2017 Componentes con vulnerabilidades conocidas

Los componentes como bibliotecas, frameworks y otros módulos se ejecutan con los mismos
privilegios que la aplicación. Si se explota un componente vulnerable, el ataque puede provocar
una pérdida de datos o tomar el control del servidor. Las aplicaciones y API que utilizan
componentes con vulnerabilidades conocidas pueden debilitar las defensas de las aplicaciones y
permitir diversos ataques e impactos.



A10:2017 Registro y Monitoreo Insuficientes 

El registro y monitoreo insuficiente, junto a la falta de respuesta ante incidentes permiten a los
atacantes mantener el ataque en el tiempo, pivotear a otros sistemas y manipular, extraer o
destruir datos. Los estudios muestran que el tiempo de detección de una brecha de seguridad es
mayor a 200 días, siendo típicamente detectado por terceros en lugar de por procesos internos.


Para obtener mayor información acerca de cada una de estas vulnerabilidades pueden bajar el documento impreso de las mismas en castellano en https://www.owasp.org/images/5/5e/OWASP-Top-10-2017-es.pdf

Espero les sea útil.



Redactado por Mauro Maulini R.

20 de septiembre de 2019

¿Por qué utilizar Cookies "Seguros"?

Uno de los problemas que presentamos los desarrolladores es el control de los Cookies usados en transacciones seguras. El primer inconveniente que se presenta cuando usamos cookies bajo protocolo SSL o TSL (el conocido HTTPS con la S al final de "secure") es que estos cookies si no tomamos las debidas precauciones en el manejo de los mismos, persisten si el usuario deja la transacción segura para continuar visitando otras áreas de nuestro sitio, lo que convierte su contenido en interceptable.

Otro de los casos que permite revelar las cookies que pasan a través de una conexión segura y que además es muy común, es aquel en el que en una misma página solicitada por HTTPS, solicitamos recursos por simple HTTP, como por ejemplo una imagen en una cabecera que se usa indistintamente en páginas seguras y no seguras. En la cabecera HTTP de la solicitud de dicha imagen el servidor envía todos los cookies del dominio que hace la solicitud en los cuales por supuesto incluye aquellos que se usan para el control de sesión y otras variables de la aplicación "ya no tan segura".

Es importante recordar que el protocolo HTTP es "stateless" o en otras palabras, no maneja estado de sesión, por lo que cada conexión se realiza como si fuera la primera. Precisamente para poder emular un estado de sesión es que las diferentes plataformas de desarrollo utilizan el concepto de un identificador de sesión (session ID), que no es más que un cookie que funciona como identificador de sesión y al cual se asocia un área de memoria en el servidor en la cual se guardan las variables de sesión.

En fin, como ya habrán podido captar, es necesario para la seguridad de la aplicación proteger los cookies, y para ello el protocolo HTTP 1.1 soporta un parámetro para cada cookie conocido como "secure flag" que al ser activado no permite que los cookies con dicho atributos sean transmitidos en conexiones HTTP simple o inseguras.

Este atributo se puede agregar por cada cookie o en general, y cada plataforma conocida lo soporta. Por ejemplo:

En PHP
Coloque en true el parámetro httponly:
setcookie( name, value, expire, path, domain, secure, httponly);

En el php.ini:
session.cookie_secure = on

En JSP /Java Server Pages:

Cookie holaCookie = new Cookie("",text);
holaCookie.setSecure(true);

En ASP.NET
Coloque en el web.config la siguiente línea:

<httpCookies requireSSL="true" /> 

Para hacerlo de forma explícita por cada cookie:

HttpCookie cookie = new HttpCookie("nombre");
cookie.Secure = True;
cookie.Value = "José";

8 de septiembre de 2019

Leer correctamente los datos provenientes del Request

Un "error" muy común de los programadores web en cuanto a la seguridad de sus aplicaciones, es acceder directamente a las variables enviadas desde las solicitudes recibidas desde la colección genéricas Request, como la clase "Request" en ASP o la variable $_REQUEST en PHP:

En ASP:
String nombre = Request["name"];

En PHP:
$nombre = $_REQUEST['name'];

Si bien esta forma de hacerlo es "correcta" lingüisticamente hablando, es bueno recordar que las variables de input tradicionales de una solicitud web suelen llegar por tres diferentes vías: por el método GET, el método POST o a través de cookies. Cuando usamos la clase Request o la variable $_REQUEST según el caso del lenguaje (ASP o PHP) estamos solicitando información en una colección genérica que agrupa las tres opciones (y otras más), y si bien es mucho más fácil y limpio de usar, este método infiere en el error por omisión, de admitir que un dato específico llegue a nuestras páginas por cualquiera de los tres métodos.



El hacker entonces podría perfectamente modificar el valor de un cookie introduciéndolo desde el URL, podría también modificar valores de un formulario que viaja por POST atacándolo desde un simple enlace enviado por e-mail. Esto se debe a que nuestra aplicación al solicitar los datos no diferencia por cual de los tres métodos están siendo enviados. Las implicaciones de poder infectar los cookies del navegador del usuario a través de un URL pueden ser enormes, pero no vamos a detallar esa práctica en este artículo.

Es imperativo, utilizar el método determinado según el acceso por el que debemos recibir los datos y evitar usar el método genérico Request. Ejemplo:

En ASP:
String nombre = Request.QueryString["name"]; // para el método GET
String nombre = Request.Form["name"]; // para el método POST
String nombre = Request.Cookies["name"]; // 

En PHP:
$nombre = $_GET['name'];
$nombre = $_POST['name'];
$nombre = $_COOKIE['name'];

En el lenguaje JSP (Java Server Pages) el problema es aún más grave debido a que el lenguaje no diferencia con el método Request.getParameter("name") si la solicitud es recibida por POST, GET o es un Cookie. En este caso se recomiendan o bien utilizar el método Request.getMethod(), que nos aclara por donde han llegado los datos, simplemente utilizar la tecnología de servlets que si diferencia entre ambos métodos con doGet(request, response) y doPost(request, response).


Redactado por Mauro Maulini R.

4 de septiembre de 2019

Confiar en que el viewState de una página en Asp.NET está cifrado: ¡Error!

Uno de los errores más comunes de seguridad de los programadores de Asp.NET que utilizan "web forms" en sus aplicaciones, es creer que el "viewState" (ese campo oculto que se puede ver en todas las páginas desarrolladas con "web forms" que contiene un cantidad apreciable de caracteres ilegible) está cifrado.

El viewState es un repositorio de información en el que se apoyan los "web forms" para guardar los datos de los diferentes componentes o áreas de una página entre llamada y llamada al servidor. Incluso es accesible desde el código y podemos guardar información allí para usarla en de forma eficiente en nuestras páginas por ejemplo:

ViewState["password"] = thepassword;

Pero no es una buena práctica colocar información sensible como un password en dicho repositorio, debido a que la creencia de que el viewState está cifrado es completamente errónea. El viewState no es cifrado entre viaje y viaje al servidor sino codificado. Si usted conoce algo de criptografía entenderá que la diferencia entre cifrar y codificar es muy obvia, caso contrario permítame explicarle de forma muy resumida un importante detalle a la hora de proteger sus datos: para descodificar solo es necesario conocer el algoritmo en el cual el texto fue codificado, mientras que pare descifrar es necesario conocer una clave de cifrado además del algoritmo y esta no viaja junto al texto cifrado. 

Por lo anterior entenderá que si sabemos el algoritmo con el que el viewState es codificado, solo tenemos que usarlo en reversa para decodificarlo. Y bien ese algoritmo es el conocido Base64. Compruébelo usted mismo introduciendo el viewState de una página Asp.NET en este decodificador de viewState gratuito en línea: http://ignatu.co.uk/ViewStateDecoder.aspx 

Ante este problema tenemos dos soluciones: 
  1. No colocar nunca información delicada en el viewState
  2. Cifrar el viewState o cifrar la información que coloquemos en él.

La primera opción no necesita explicación, la segunda si amerita de algo de información adicional. El viewState, desde la versión de Asp.NET 2.0 en adelante también puede ser cifrado. Usted puede decidir hacerlo en absolutamente todo el sitio web colocando el siguiente atributo en la sección <pages> del archivo web.config:

<pages viewStateEncryptionMode="Always">

Sin embargo hay que tomar en cuenta que el cifrado puede reducir la velocidad de respuesta sobretodo si estamos en un sitio de alto consumo, por lo que podemos también utilizar el cifrado del viewState a nivel de cada página colocando el siguiente atributo en la cabecera <% @ Page >

<% @ Page Language="C#" AutoEventWireup="true" CodeBehind="login1.aspx.cs" ViewStateEncryptionMode="Always" %>

Espero que este truco e información sean de utilidad para los desarrolladores en Asp.NET.

6 de julio de 2017

¿Qué es la autentificación bidireccional y para qué sirve?

No se trata de explicar en este artículo lo mal que la han pasado desde hace ya más de dos o tres años, una innumerable cantidad de instituciones de todo tipo, debido a la proliferación de tipos de ataques "client side" como el Phishing, XSS, Pharming, y otros que no vamos a mencionar por lo largo de la lista, dirigidos específicamente a sus clientes y usuarios. Pero quizás no es tan obvio como lo primero entender que todos estos ataques pudieron haber sido reducidos en un alto porcentaje si se hubieran implementado a tiempo soluciones de autentificación bidireccionales.

¿De qué trata este nuevo término? La idea es muy simple:
No se trata solo de que la aplicación web autentifique y valide que el usuario es quien dice ser, se trata además de que el usuario pueda de igual forma validar y autentificar que la aplicación web sea la que aparentemente es.

Lo extraño de esto es que a pesar de ser una idea básica y muy simple, ha sido implementada en muy pocas aplicaciones y el sufrimiento del usuario ante las técnicas de robo de información y usurpación de atributos de uso y acceso continúa tan vivo como siempre.

Pues bien, para que vean que no es tan compleja la idea y menos su aplicación voy a dar un ejemplo.

Para empezar el banco o institución A, en el proceso de registro de sus usuarios, agrega una simple rutina mediante la cual cada uno de ellos seleccione de una librería de imágenes simples y fáciles de recordar, aquella que más se identifique con él. No quiero complicar esto en este momento, pero el usuario podría inclusive modificar algunos aspectos de la interfaz de la aplicación, "personalizándola", y asociar por ejemplo a la imagen "una frase célebre".

Luego de este proceso el usuario para ingresar a la interfaz privada de la aplicación debe pasar por un sistema de ingreso algo diferente a lo convencional que podemos denominar "doble capa de acceso". Este proceso no significa más que proporcionar los pasos de acceso en dos fases:
  1. En la primera fase el usuario insertará su identificador de usuario (login) y algún otro detalle a discreción del banco A, como por ejemplo el número o nombre de la agencia en la que abrió su primera cuenta. 
  2. Mediante los datos proporcionados en la primera fase por el usuario, el sistema pasa a una segunda fase en la que le muestra a este los cambios realizados a la interfaz y la imagen seleccionada por él en el proceso de registro. Solo si los anteriores corresponden entonces el usuario debe procede a colocar la pieza más crítica de sus datos: su clave de acceso.
Por tanto si no aparecen correctamente la interfaz y la imagen que el usuario seleccionó, seguramente estará ante la presencia de una página falsa y no procederá a insertar el resto de los datos de acceso. Un par de buenos mensajes como: "Si usted no puede ver su imagen de identificación o si nota algún cambio en su interfaz, no coloque su clave de usuario bajo ningún pretexto", son suficientes para que el usuario sepa que hacer en caso de un intento de phishing.

¿Es este proceso demasiado complejo de implementar?

Una de las principales razones que se esgrimen para no hacerlo, es que el proceso de ingreso debe ser realizado en dos etapas o dos páginas, con lo que se hace algo más lento. Esta es en realidad una excusa muy burda, ya que el usuario agradece y con creces este proceso que ya ha sido implementado en algunas instituciones y lo agradece de forma radical, es decir utilizando esas aplicaciones mucho más que otras quizás de mayor funcionalidad pero no tan seguras (al menos en apariencia).

Otro punto a favor de la idea y en contra de la anterior excusa es que este doble proceso puede ser simplificado radicalmente con técnicas actuales como AJAX, que además le ponen el proceso de emulación del sitio al atacante algo más cuesta arriba. No es tan sencillo hacer phishing de un sitio que cambia según lo que agregamos en él y mucho menos si esperamos ver cambios que solo la aplicación y nosotros conocemos.

Además el hecho de que el campo de introducción de la clave aparezca en una segunda fase y solo si los primeros datos coinciden, reduce drásticamente la superficie de ataque hacia los campos más sensibles de una segunda etapa.

Es así de sencillo, pero a pesar de ello las estructuras mentales de los que toman este tipo de decisiones siguen prefiriendo pelear semi-desnudos contra el el phishing y otros flagelos. No me gustaría tener que darle la razón a los hackers cuando mencionan como un hecho que "los code grinders tienen la creatividad de una rana".

Hasta la próxima!

8 de febrero de 2016

OWASP Dirbuster - Escaneando directorios y archivos ocultos de una aplicación web

Uno de los problemas más comunes a la hora de programar es la creencia de que una página o directorio al que no lleva ningún enlace, jamás serán vistos. Pues si esto fuera cierto, lo sería solo en parte, y esa parte dependería de la facultad del programador de escoger nombres complejos y difíciles de adivinar por los expertos en seguridad y por supuesto los atacantes.

La idea de este artículo, es la de encarar al programador con herramientas capacitadas para descubrir la mayor parte del árbol de directorios de nuestra aplicación así como muchas de las rutinas y páginas de esta, por el simple motivo de que todos los programadores usamos casi las mismas herramientas, venimos de una misma corriente de pensamiento o leemos los mismos artículos y vemos las mismas películas y libros. En pocas palabras que debido a que usamos Macromedia Dreamweaver o Visual Studio, nombramos los directorios y archivos de una forma específica.

Este tipo de herramientas trabajan con diccionarios de nombres de archivos y directorios (en varios idiomas) los cuales se prueban solos o con extensiones comunes. Por ejemplo, este tipo de aplicaciones no tardaría prácticamente nada en descubrir un archivo cuyo nombre fuera "admin.zip" en donde un programador incauto podría haber dejado copia de respaldo de todas las rutinas del backoffice de su aplicación. Ejemplos como este hay muchos y algunos espeluznantes, ya que los aciertos no necesariamente son tan sencillos de imaginar como el anterior.

En fin, la intención es que ustedes mismos puedan descubrir cuan vulnerables son esas rutinas administrativas que damos por ocultas o inalcanzables desde la web, y para ello no hay mejor maestro que la experiencia. Y precisamente para que podamos experimentar con nuestras aplicaciones web,  OWASP (quien más podría ser) ha puesto uno de sus proyectos  de seguridad al alcance de todos, y en este caso se trata específicamente del Proyecto "Dirbuster". Disbuster (según palabras de sus creadores) es una aplicación Java multi hilo diseñada para obtener por fuerza bruta los nombres de directorios y archivos en servidores de aplicaciones web.

Puede descargarla en formato comprimido zip en este enlace.

Recuerde, Dirbuster no es más que una aplicación eficiente que nos muestra las posibles fallas de seguridad en la forma en que nombramos nuestras rutinas y directorios, pero cualquier herramienta de análisis de vulnerabilidades seria cuenta con rutinas para el mismo fin y algunas veces suelen ser muy superiores a esta.

Ejecute Dirbuster sobre su aplicación web de forma local, para evitar demoras por problemas de ancho de banda o bloqueo de firewalls, y verá como su forma de nombrar sus archivos y directorios cambiará para siempre.

Hasta la próxima...

3 de octubre de 2014

OWASP Top 10 Mobile Security Risks

Nuevamente OWASP pone a disposición de los programadores interesados en desarrollar código seguro (debiéramos ser todos, pero tristemente no es así) un proyecto interesante, pero esta vez haciendo énfasis en la programación de aplicaciones móviles. Este proyecto no es más que el OWASP Mobile Security Project, que al igual que el Web Application Security Proyect ofrece una serie de utilidades, consejos y material didáctico muy interesante. 

Dentro de los recursos que ofrece sin fines de lucro este proyecto, no podía faltar la lista de los 10 riesgos más importantes a tomar en cuenta por los programadores al desarrollar aplicaciones para plataforma móviles.

Estos riesgos según OWASP son los siguientes:


Es una lista muy interesante que todo programador de aplicaciones móviles debiera conocer independientemente de a plataforma para la cual desarrolle. La lista fue publicada en Junio 2013, y como es lógico todos estos puntos forman parte también del curso de Seguridad de Aplicaciones Web y Móviles.


22 de septiembre de 2014

Desarrollo de aplicaciones móviles con frameworks multiplataforma: lo bueno, lo malo, lo feo...

A lo largo de estos últimos dos años hemos visto como en el ámbito de desarrollo de aplicaciones móviles, las herramientas multiplataforma han jugado un papel interesante para muchos programadores abrumados por la cantidad de diferentes lenguajes, APIS y kits de desarrollo propuestos por las diferentes plataformas móviles, quienes queriendo quizás adoptar un estándard se encuentran con que el mismo aún no existe.

Al querer abarcar mayor cantidad de público para sus aplicaciones, los desarrolladores se encuentran con por lo menos tres o cuatro diferentes plataformas, cada una con un sistema operativo, lenguaje de programación y un kit de desarrollo completamente diferente, por lo que en muchos casos en vez de dedicarse al desarrollo de aplicaciones nativas para cada sistema, optan por desarrollar con herramientas multi-plataforma como Apache Cordova/PhoneGapAppcelerator's TitaniumSencha Touch y otras.

Aún así no todo lo que brilla es oro, y existe una larga lista de asuntos pendientes por aprobación que hacen de las aplicaciones desarrolladas con esta técnica algo como una especie "secundaria". Indudablemente la opción de desarrollar con esta técnica depende directamente del tipo de aplicación a desarrollar y del propósito específico de cada equipo de desarrollo.

Los pros del desarrollo multi-plataforma de aplicaciones móviles pueden resumirse en los siguientes:


  • Código reutilizable:Solo tienes que escribir el código una vez y aplicarlo en cada plataforma. Esto indudablemente significa una reducción drástica de tiempo y esfuerzo a la hora de calcular los costos de desarrollo.
  • Fácil acceso a Plug-ins:Los principales entornos de trabajo le permitirán utilizar extensiones y módulos para mejorar el alcance funcional de su aplicación.
  • Facilidad de trabajo en grupo:La gran mayoría de los entornos de trabajo multiplataforma utilizan Javascript, HTML5 y CSS3, haciendo más sencillo el proceso de aprendizaje y la comunicación entre desarrolladores ya que estos son estándares muy conocidos y ampliamente utilizados.
  • Reducción de costes:Aunque ya mencionamos esto de pasada en el primer punto, la razón primordial de este punto es la no dependencia de programadores específicos para cada ecosistema y por consiguiente el pago por separado por cada versión de la aplicación según sistema operativo.
  • Integración con la nube:Marcos de desarrollo como PhoneGap, Secha y Titanium ofrecen una fácil integración con los servicios de nube, lo que significa que una vez llevado a código el proceso, cualquier versión de la aplicación manejará esta integración casi transparentemente.


Los contras del desarrollo multi-plataforma de aplicaciones móviles también deben tomarse en cuenta:


  • Su aplicación podría no soportar todas las características esperadas para cada sistema operativo:Este es un serio problema con las aplicaciones multi-plataforma, o bien se tienen que ajustar a la velocidad con la que el entorno multi-plataforma se adapte a los nuevos cambios en las versiones de los sistemas operativos, o simplemente hay opciones que no existen o no soporta un sistema operativo determinado. El típico problema de tener que escoger el mínimo común divisor, o decidir fragmentar el código en opciones diferentes para cada sistema operativo.
  • Restricciones de la herramienta:En algunos casos por ejemplo en el caso de adoptar librerías de terceros, sobretodo aquellas desarrolladas para lectores de tarjetas, impresoras y componentes externos hay serias limitaciones que en las aplicaciones nativas simplemente no existen.
  • Su código podría ser algo más lento:Generalmente el código generado en este tipo de herramientas se apoya en una "traducción" a código nativo, pero en ocasiones el intérprete debe trabajar en tiempo real, haciendo que exista una capa adicional de procesamiento y por ende el código ejecutado sea algo más lento.
  • Carecen de gráficos y soporte para 3D:Si piensa desarrollar algún tipo de juego 3D, definitivamente estas herramientas no son la mejor solución. También podría tener problemas si necesita utilizar funciones criptográficas avanzadas ya que cada sistema operativo y lenguaje poseen características que diferencian la parametrización de las líbrerías de cifrado aunque se usen los mismos algoritmos.
  • Alta dependencia del prooveedor:La mayoría de los entornos de desarrollo multi-plataforma usan su propio sub-conjunto JavaScript. Esto significa que si en un futuro deseara cambiar de herramienta le sería bastante difícil.


En conclusión, queda claro que los entornos de desarrollo "híbridos" tienen aún muchos retos por delante, pero si dentro de las necesidades de su empresa no se afectan por los inconvenientes de este tipo de herramienta, pueden ser una buena opción si se necesita una solución rápida y económica.

Redactado por Mauro Maulini R.

9 de julio de 2014

Cómo implementar el encabezado HTTP Strict Transport Security en código

Si el video de OWASP del anterior post sobre HSTS (HTTP Strict Transport Security) los dejó con algo de espectativas acerca de la implementación del encabezado de HTTP para el uso estricto de HTTPS, entonces lo que viene a continuación les interesa.
Si bien se puede implementar el encabezado a nivel de servicio web agregando en la configuración de las diferentes plataformas (Apache, IIS, nginx y otros) un "header", a veces es mucho más práctico agregar la cabecera directamente a nivel de aplicación o en las rutinas específicamente sensibles.
Cómo el vídeo no nos muestra el código para ello a continuación algunos ejemplos en diferentes lenguajes y/o plataformas:

Implementación en PHP.
// Usar HTTP Strict Transport Security para forzar al cliente a usar 
// conexiones seguras

$use_sts = true;
 
// iis sets HTTPS to 'off' for non-SSL requests
if ($use_sts && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
    header('Strict-Transport-Security: max-age=31536000');
} elseif ($use_sts) {
    header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], true, 301);
    // we are in cleartext at the moment, prevent further execution and output
    die();
}  
Implementación en Perl CGI.
# Usar HTTP Strict Transport Security para forzar al cliente a usar
# conexiones seguras
use CGI;
use URI;
 
my $q   = new CGI;
my $url = URI->new($cgi->request_uri)
my $use_sts = 1;
 
if ($use_sts and $url->scheme eq 'https') {
    print $q->header('Strict-Transport-Security' => 'max-age=31536000'); 
} elsif ($use_sts) {
    $url->scheme('https');
    print $q->redirect(status => 301, location => $url);
}  
Implementación en Ruby on Rails.
class ApplicationController < ActionController::Base
  before_filter :ensure_proper_protocol
 
private
  def ensure_proper_protocol
    if request.ssl?
      response.headers['Strict-Transport-Security'] = 'max-age=31536000'
    else
      redirect_to "https://" + request.host + request.request_uri, :status => 301
    end
  end
end  
Implementación en C# / ASP.NET. Código en archivo global.asax
// Usar HTTP Strict Transport Security para forzar al cliente a usar 
// conexiones seguras
protected void Application_BeginRequest() { switch (Request.Url.Scheme) { case "https": Response.AddHeader("Strict-Transport-Security", "max-age=31536000"); break; case "http": var path = "https://" + Request.Url.Host + Request.Url.PathAndQuery; Response.Status = "301 Moved Permanently"; Response.AddHeader("Location", path); break; } }  
Implementación en ColdFusion Markup Language (CFML).
<!--- Usar HTTP Strict Transport Security para forzar al cliente a usar
conexiones seguras --->
<cfset use_sts = true>
 
<cfif use_sts is "True">
    <cfif cgi.https is "on"> 
        <cfheader name="Strict-Transport-Security" value="max-age=31536000">
    <cfelse> 
        <cfheader statuscode="301" statustext="Moved permanently">
        <cfheader name="Location" value="https://" + CGI.SERVER_NAME + CGI.SCRIPT_NAME + CGI.QUERY_STRING>
    </cfif>
</cfif>  
Implementación en JavaServer Pages (JSP) o Java Servlets.
// Usar HTTP Strict Transport Security para forzar al cliente a usar 
// conexiones seguras
boolean use_sts = true; if(use_sts) { if(request.getScheme().equals("https")) { // Send HSTS header response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubdomains"); } else { // Redirect to HTTPS response.setStatus(301); String url = "https://" + request.getServerName(); if(request.getPathInfo() != null) { url = url + "/" + request.getPathInfo(); } if(request.getQueryString() != null && request.getQueryString().length() > 0) { url = url + "?" + request.getQueryString(); } response.setHeader("Location", url); } }

Espero les sea suficientemente útil y como podrán apreciar no es nada complicado.

12 de agosto de 2013

Las aplicaciones móviles son fáciles de "decomplilar"

Todos los programadores sabemos a ciencia cierta que las aplicaciones son de una manera u otra "decompilables", es decir podemos obtener con las herramientas adecuadas el código fuente de la aplicación para alterarlo y/o modificarlo (y si no lo sabemos es hora de despertar). Sin embargo cuando una aplicación se encuentra en formato binario el proceso de ingeniería de reverso es mucho más complejo, pero no es este el caso cuando hablamos de aplicaciones móviles desarrolladas en lenguajes como Java o .NET que en realidad no se encuentran en código binario sino en formato intermedio conocidas como "bytecodes" en el caso de Java e "intermediate language (IL)" en el caso de .NET

En el caso de iOS, decompilar las aplicaciones es algo más complejo ya que son necesarios decompiladores costosos y conocimiento avanzado de las técnicas, sin embargo tampoco es un trabajo imposible de realizar.

Si dudan de lo anterior, solo necesitan seguir este enlace http://www.youtube.com/watch?v=3tioug10tuo  que los llevará a un video de cómo decompilar aplicaciones para Android.

Por lo tanto es muy importante entender que debemos ser más eficientes y cautos a la hora de hablar de seguridad en el caso de las aplicaciones móviles, ya que ponemos en riesgo nuestro trabajo (lo cual sería lo menos preocupante) así como la "lógica de negocio" de la empresa para la que desarrollamos la aplicación o lo que es peor aún los datos confidenciales de los usuarios que la utilicen.

Hay una buena cantidad de consejos que deberemos tomar en cuenta mientras desarrollamos aplicaciones móviles, pero quizás el más importante de todos pudiera ser si vamos a desarrollar aplicaciones seguras, no dejar jamás datos específicos "hard coded" dentro de la aplicación. Hablando en lenguaje más simple, no debemos jamás dejar una clave de cifrado, una cadena de conexión a un servidor o base de datos o un algoritmo de negocios en texto plano en nuestro código.

Para evitar lo anterior, todo dato sensible debe ser cifrado y la clave de cifrado debe ser proporcionada por alguna constante del entorno del dispositivo que se mantenga permanentemente pero que sea diferente en otros dispositivos. Por tanto si un amigo de lo ajeno obtiene nuestro código, le será mucho más difícil obtener nuestros datos o lo de nuestros usuarios, ya que cada usuario tendrá una clave de cifrado única.

Lo anterior haría más difícil la obtención de la clave pero no evitaría que el atacante la obtuviera, sin embargo debería repetir un procedimiento complejo para cada usuario o tener acceso directo al dispositivo de cada uno de ellos, lo que reduce drásticamente la superficie de ataque.

Muchos otros son los consejos de seguridad para aplicaciones móviles, como por ejemplo obtener un buen ofuscador de código si realmente se considera de alta prioridad la seguridad, como en el caso de aplicaciones bancarias o de pago. En próximos artículos hablaremos más sobre esto...

Hasta la próxima...

9 de octubre de 2012

OWASP Appsec Tutorial Series - Episodio 4: Seguridad de Transporte Estricta

El siguiente episodio de la serie OWASP Tutorial Appsec describe la importancia de utilizar HTTPS para toda la comunicación sensible, y cómo el encabezado HTTP Strict Transport Security (estricta seguridad de transporte) se puede utilizar para garantizar una mayor seguridad, mediante la transformación de todos los enlaces HTTP a HTTPS automáticamente en el navegador.

Espero les sea útil...
Hasta la Próxima.


8 de octubre de 2012

Comenzando con Seguridad para ASP.NET

Recientemente llegó a mis manos un libro muy interesante llamado "Beginnig ASP.NET Security" de la serie de libros de WROX Programmer to Programmer, que no puedo dudar ni por un segundo en recomendarles en este artículo.

A pesar de ser un libro para programadores, su lenguaje es bastante claro en los primeros capítulos para que cualquier programador de la excelente plataforma de desarrollo web de Microsoft, pueda entender sin inconvenientes todos los "intrigulis" de la seguridad web.

En este libro encontrará, como proteger sus aplicaciones en ASP.NET basándose en el documento OWASP TOP 10, que es el documento creado por la Open Web Application Security Proyect para implantar un estándar de referencia acerca de los 10 tipos más comunes de vulnerabilidades detectadas en aplicaciones web. Por tanto el libro toca cada una de las anteriores y muestra las diferentes formas de proteger las aplicaciones, y además permite seguir paso a paso los métodos de cifrado mas eficientes utilizándolos desde la plataforma .NET para proteger los datos de nuestros usuarios.

También maneja un amplio capítulo acerca de la seguridad en el IIS (Internet Information Server) que es la plataforma de servicios con la que se ofrece lógicamente ASP.NET (entre otros). Además uno de sus más extensos capítulos está dedicado a la seguridad de Web Services, bajo WCF (Windows Communication Foundation) a través de RIA, SOAP y otros protocolos de servicios web. Por último ofrece todo un capítulo que explica como adaptar los procesos de seguridad básicos al ASP.NET MVC (Model View Controller).

El libro está actualizado a la plataforma de ASP.NET 4, pero sus capítulos están adaptados para ser utilizados en versiones anteriores.

Es mi recomendación que este libro se convierta en una referencia de seguridad obligada para todo proyecto en ASP.NET.

Hasta al Próxima...

8 de julio de 2012

¿Cómo mejorar el "hashing" de las claves de acceso sin molestar al usuario?

Siempre hemos mencionado que definitivamente "No debemos guardar claves de acceso en nuestras bases de datos". Sin embargo la técnica intermedia de guardar el HASH de dichas claves hasta ahora ha sido una solución interesante. Aún así, ya tampoco es muy seguro utilizar esta técnica de forma simple debido a los Ataques por Diccionarios de HASH y los Raibow Tables.

También es necesario acotar se ha demostrado que ciertos tipos de hash como el MD5 presentan conflictos de colisiones, por los cuales los certificados SSL que utilizaban este hash quedaron fuera de circulación debido a que podían ser emulados buscando una colisión específica cualquiera que aunque no representara el certificado original, permitiera emularlo.

Por lo tanto la anterior debilidad del MD5 no solo es válida para los certificados SSL sino para cualquier sistema que lo utilice y por consecuencia para cualquier proceso de validación de clave que se base en este algoritmo. Sin embargo las técnicas de "salting" basadas en agregar una cadena específica a la clave de acceso antes de calcular el hash todavía pueden "salvarle el pellejo" en caso de un ataque, ya que aunque el atacante pueda encontrar un texto que colisione con el md5, este no pasaría la validación ya que al agregarle el "salt key" y calcular el hash este cambiaría nuevamente.

Mi recomendación es que definitivamente no cuente con el md5 en sus desarrollos a futuro y trate de reemplazarlo en los existentes. Sin embargo cuando ya usted posee una gran cantidad de usuarios es difícil cambiar de algoritmo de hashing sin obligarlos a cambiar de clave. Usted no puede cambiar el algoritmo simplemente ya que no posee (al menos no debería) las claves originales sobre las que realizar el cálculo del nuevo hash.

Para esto hay un remedio muy eficiente que se basa en utilizar la técnica de "salting" aplicándola al hash que ya posee y calculando un nuevo hash (preferiblemente no md5) sobre el resultado.  

Ejemplo:
  • Imaginemos que el usuario utilizó una clave como por ejemplo: Hey@you123
  • Usted no conoce dicha clave pero posee el hash MD5 de la misma que es lo que hasta ahora ha utilizado para validar el ingreso del usuario a su sistema: 27f97cb40faf0a16e09dde2a1c2b25b8
  • Agréguele una palabra (salt key) a dicho hash como por ejemplo:  MySaltKey28
  • La cadena resultante sería en este caso:  27f97cb40faf0a16e09dde2a1c2b25b8MySaltKey28
  • Ahora calcule un hash nuevamente con el algoritmo que prefiera (en este caso por ejemplo hemos utilizado SHA1):
    4cd80c13b53a136f62732c6a1e3f88200192edbf
  • Guarde el nuevo resultado en otro campo y deshágase de todo vestigio del viejo hash md5.
Recuerde que cada vez que un usuario quiera validar usted deberá repetir el proceso empezando por calcular el MD5, agregar el "salt key" y calcular el SHA1, aunque esto no debiera ser nada complejo si usted prepara una rutina eficiente que siga los pasos requeridos.

Usted habrá utilizado dos técnicas de hashing que juntas pueden garantizar la seguridad de sus usuarios:  "Hash Salting" e "Interacción entre diferentes tipos de hash". De esta forma si el nuevo hash cayera en manos indebidas ya no podría obtenerse una colisión ni utilizarse un ataque por diccionario.

Usted puede además complicar este proceso tanto como lo desee agregando más interacciones o cálculos recursivos de hash, pero deberá pensar en el tiempo y recursos que podría tomar un cálculo demasiado complejo en relación a la cantidad de usuarios y cantidad de validaciones que requiera.

Hasta la próxima...

13 de junio de 2012

¿Qué es una inyección LDAP?

Si bien las inyecciones LDAP no son muy comunes, pueden ser una de las más peligrosas vulnerabilidades en la web. Para empezar necesitamos aclarar para aquellos que no entienden el término que significa el acrónimo LDAP. Lightweight Directory Access Protocol o traducido al español Protoloco Ligero de Acceso a Directorio es el que se encarga del control de las listas de control de acceso de un dominio o red determinado. Para los amantes de Windows, quizás hayan escuchado hablar más de Active Directory que no es sino la versión de LDAP del entorno de Windows. Otros sitemas operativos utilizan versiones de OpenLDAP, Novell Directory Services, Apache Directory Service y otros.

Por tanto una vez dicho lo anterior explicamos la forma en que se puede realizar una inyección LDAP. Aunque como dijimos al principio el ataque de inyección LDAP no es muy común, se parece de alguna forma a un ataque de inyección SQL, ya que para los efectos de una aplicación web, el acceso a LDAP es muy parecido al acceso a una base de datos, la diferencia estriba en que con los conocimientos necesarios, en vez de atacar a un servidor SQL el hacker ataca al sistema de validación de usuarios, para intentar así cambiar la permisología de estos y hasta crear usuarios con los cuales acceder luego a otros equipos o a zonas más sensibles del dominio.

Uno de los preferidos vectores de acceso son los formularios de búsqueda de usuarios. Imaginesmos un simple formulario que solicite el "login" o identificador de usuario para mostrar algún dato de este.

<input type="text" size=20 name="nombre">Introduzca el nombre de usuario a buscar</input>

Al igual que en el caso del SQL injection, el programador toma el contenido del campo nombre sin desinfectarlo y lo introduce en una consulta como:

string nombre = Request.Querystring("nombre")
String ldapSearchQuery =  "(cn=" + nombre +")";

Si el usuario coloca el nombre "alberto" esto produciría la cadena de consulta "(cn = alberto)". Pero que sucedería si el usuario insertara en el campo nombre la cadena "alberto)(|(password=*)" .En este caso se produciría la cadena resultante "(cn=alberto)(|(password=*))" que devolvería el password del usuario alberto.

Como podrá intuir, otro de los "dulces" preferidos de los hackers son los formularios de entrada que validan a sus usuarios mediante LDAP. La que acabamos de mostrar no es más que una simple técnica de ejemplo. El atacante experimentado suele introducir rutinas completas en una vulnerabilidad como la que acabamos de mostrar.

¿Cuál es el remedio? Validación estricta de los datos de entrada o lo que conocemos como desinfección de parámetros del lado del servidor. De nada sirve validar los datos con Javascript en estos casos, el atacante utiliza formularios forjados o simplemente deshabilita el javascript en su navegador.

Pareciera repetitivo, pero existen muy buenas librerías de desinfección de parámetros para cada uno de los lenguajes de uso común actualmente. Usted también puede verificar las soluciones que ofrecen las extensiones PHPFilter para PHP, Microsoft Web Protection Library y los Proyectos AntiSami de OWASP entre muchas otras.

¿Que es un ataque de DDoS?

Escribo este artículo precisamente para aclarar una serie de preguntas de las que he sido objeto en días en que se ha desatado la mayor guerra cibernética de la historia. Es estos últimos día debido al caso WikiLeaks, hemos escuchado y leido acerca del término DDoS en repetidas ocasiones, asociado a varios ataques realizados a sitios importantes de uno y de otro bando de la confrontación y es necesario aclarar qué significa.

Para empezar hablemos de DoS, aunque parezcan las siglas de un viejo y recordado sistema operativo hay que notar que la "o" del medio en este acrónimo está en minúsculas intencionalmente, precisamente porque era una manera de diferenciar a este tipo de ataque conocido como Denial of Service (Negación de Servicio) del viejo sistema operativo DOS.

La técnica de DoS en palabras simples se basa en solicitar un recurso a un servidor web desde varias locaciones simultáneamente y una gran cantidad de veces de forma que el servidor no pueda atender a otras solicitudes.

Tomemos el clásico el ejemplo del cantinero que puede atender a una serie de clientes en una barra de un bar y de repente se ve completamente agobiado ante las solicitudes de dos equipos completos de fútbol que han llegado a festejar a bar, dejando mal atendidos o desatendidos a los clientes habituales.

En los casos del DoS tradicional la idea es solicitar un recurso muy "pesado" en cuanto a tamaño y potencia de CPU mediante un simple URL que no tiene prácticamente costo de ancho de banda. Sin embargo esta técnica ya no es efectiva ya que cualquier firewall o cortafuegos puede controlar la cantidad de ancho de banda utilizado en satisfacer a un solo cliente y si esta es excesiva bloquea automáticamente las solicitudes provenientes de dicho cliente.

La técnica fue perfeccionándose y los ataque se basaron ya no en solicitar recursos complejos, sino en ubicar una deficiencia en la programación que indujera a errores no controlados (Exploits) para reproducirla la cantidad de veces necesaria para que el servidor quede sin recursos de memoria, espacio en disco u otro recurso necesario. Sin embargo al corregir la vulnerabilidad el método quedaba obsoleto y solo era efectivo en servidores que no hubieran corregido el problema, lo que reducía la efectividad con el tiempo.

A veces la aplicación de la técnica podía ser tan sencilla como ejecutar un error en código tal cantidad de veces que el archivo de registro de sucesos de errores crecía tanto que ocupaba todo el espacio disponible en el equipo del servidor.

Pero esos eran tiempos pasados, el problema ahora es otro aunque los métodos se basan en el mismo principio. Cuando hablamos de DDoS (con una D más) nos referimos a Distributed Denial of Service.

Para hablar de esta vertiente mucho más compleja del DoS original es necesario explicar que es una BotNet. El término proveniente de "Robots Net" o red de robots y se usa para denominar a una red de computadores que han sido infectados con un troyano (o rootkit) que permite al hacker utilizarlos como zombies para enviar SPAM, generar ataques de fuerza bruta para obtención de credenciales y otras  técnicas del lado oscuro, como por ejemplo el DDos. Las BotNets usualmente "pertenecen" a un Hacker o grupo de hackers específico. Mientras más grande la BotNet más "poderoso" es el hacker.

Efectivamente en suma el DDoS no es más que un ataque de negación de servicio pero distribuido, ejecutado a través de una red de computadores zombies. Sin embargo es muy difícil de detener, ya que no se puede diferenciar el origen del ataque para bloquear las solicitudes ya que estas difícilmente se pueden aislar de las de los clientes reales. Además el hacker utiliza a sus zombies como batallones que atacan en grupos y a diferente flancos (diferentes recursos).


Todos estos zombies en la mayoría de los casos son controlados mediante instrucciones colocadas en un canal de IRC (Internet Relay Chat), una de las más antiguas formas de chatear en la red. Ellos simplemente se conectan a un puerto determinado en un servidor con un dominio flotante,  obteniendo instrucciones cada tantos minutos y ejecutándolas.

Aunque estoy a favor de la libertad de expresión y especialmente en el ciberespacio, creo que lo que debiera preocuparnos algo más la cantidad de  equipos infectados que están interviniendo en esta guerra virtual. Usted pudiera estar interviniendo en ella mientras lee este artículo sin saberlo.

Hasta la próxima...

¿Que es el Cross Site Request Forgery (CSRF)?

Cuando apenas empezamos a controlar el dolor de cabeza generado por el XSS (Cross Site Scripting) nos vemos las caras con otra amenaza que aunque lleva tiempo en el ruedo de la seguridad web últimamente pareciera tomar auge definitivo para convertirse en una de las peores amenazas de los próximos años. El CSRF o Cross Site Request Forgery también conocido como XSRF, al contrario del XSS que explota la confianza del usuario en un sitio en particular, explota la confianza del sitio en un usuario en particular. Convencionalmente el CSRF utiliza a un usuario validado para a través de este introducir solicitudes "válidas" que modifiquen el comportamiento de la aplicación a favor del atacante.

En palabras simples, el atacante usa a la victima para que sea ella misma la que realice la transacción dañina cuando la víctima se encuentra validada en el servidor y en la aplicación específica. El proceso es simple, muchos usuarios no finalizan correctamente (o no pueden hacerlo) sus sesiones en las aplicaciones bancarias o de otra índole que puedan ser afectadas por esta vulnerabilidad y las mantienen activas mientras navegan otros sitios, más aún en tiempos en que las pestañas de los navegadores son muy utilizadas, por tanto desde cualquier otra ventana en el navegador se pudiera inducir al usuario a pulsar un enlace con una orden a sitios en los que el usuario ha ya autenticado, para que el usuario ejecute sin saberlo la acción de ataque.


¿Cómo evitar el CSRF?

Uno de los mejores consejos para los usuarios es simplemente hacer una sola cosa a la vez cuando se trate de manejar sus cuentas en línea, y salir de las aplicaciones haciendo el respectivo "logout" que casi siempre significa pulsar un simple botón de "salir" o "abandonar la aplicación". Por ejemplo: No pulsar un enlace recibido mediante en "Messenger" o en otra pestaña del navegador cuando estamos utilizando la aplicación de nuestro banco puede ser la mejor solución imaginable ante un ataque de CSRF.

Sin embargo para el programador es un poco más difícil prevenir este tipo de ataque. Lo primero indudablemente es darle al usuario la posibilidad de cerrar su sesión. Aunque parezca raro, usted se asombraría de la cantidad de sitios y aplicaciones que no disponen de esta opción una vez que el usuario ha validado su ingreso a las mismas.

En cuanto a código, el mejor de los aliados para controlar el CSRF es el control estricto del Session TimeOut de la aplicación, de forma que si el usuario no se conecta nuevamente y olvida "salir" correctamente de la aplicación, el servidor deberá en un lapso corto de tiempo dar dicha sesión por finalizada.

Otra eficiente forma de controlar el CSRF es la introducción de un "token" dinámico adicional en las solicitudes del cliente que se asocia a la sesión de éste en el servidor y se agrega a todos los enlaces y solicitudes. Este token será siempre diferente por sesión y por usuario por lo que de esta forma se le hace más difícil al atacante tratar de emular el enlace necesario para efectuar el ataque debido a que el token es variable.

Muchos programadores hacen públicos los enlaces privados, y esta es también una razón para que los atacantes utilicen estos enlaces privados a discreción. Los enlaces privados solamente deben ser vistos si un usuario ha validado su sesión y jamás deben ser expuestos en e-mails o páginas públicas. Esto también parece obvio pero se asombraría al ver la cantidad de enlaces "privados" de un sitio que se pueden obtener mediante técnicas de GHDB (Google Hacking Database).

Hasta la próxima.

Entradas populares