lunes, febrero 23, 2009

Encriptación de imágenes/ficheros en servidor con asp.net

Las clases Crypto.cs y CryptoProvider.cs las podéis descargar desde aquí.

En este artículo vamos a explorar un aspecto que siempre me ha tenido preocupado a la hora de trabajar con imágenes o ficheros subidos por los usuarios en aplicaciones en internet con asp.net. El problema es la encriptación de esos archivos para evitar que alguna persona pueda linkarlos y acceder a ellos. 

La raiz del problema
Cuando programamos una aplicación ASP.NET para que se puedan subir archivos a una carpeta, debemos darle permiso al usuario con el que se ejecuta el proceso ASP.NET para que escriba en esa carpeta y para leer la información (para su poste
rior visionado). Una alternativa es realizar un impersonate sobre el proceso (algo que nunca me ha gustado). Otras alternativas que propone la gente es darles nombres extraños, ubicaciones extrañas, etc. pero no dejan de ser soluciones de ocultación que no solventan el problema. La alternativa que vamos a ver en este post es la de realizar una encriptación del fichero subido con el algoritmo AES-Rijndael de forma que aunque un potencial usuario malintencionado gane acceso al fichero subido, no sea capaz de averiguar su contenido.

Las dos partes del problema
La encriptación de ficheros en servidor tiene dos partes bien diferenciadas que tenemos que solventar:
  1. Encriptación al subir el fichero
  2. Desencriptación al vuelo al visionar/descargar el fichero sin dejar ninguna copia desencriptada en el servidor.
El flujo de información del proyecto es el siguiente:
















Vamos al lío, subiendo archivos al servidor...
La primera parte es subir archivos al servidor. Cada uno utilizará el método que quiera, yo he utilizado el componente FileUpload que viene con los controles de servidor. Cuando se pulsa el botón de subir el archivo, se ejecuta el siguiente código, en el que se realizan las siguientes acciones:
  • Se comprueba que la extensión sea pdf
  • Se comprueba que el tamaño no exceda los 10MB (estoy hay que configurarlo en el web.config para que el servidor lo admita)
  • Se sube el fichero con un nuevo nombre único
  • Se encripta el fichero

















La línea clave es objencriptador.EncriptarFichero(), ¿qué es lo que hace esta función? Esta función es la encargada de encriptar el fichero pasado en el primer parámetro, guardarlo con el nombre del segundo parámetro y eliminar el original.





















Este método se encuentra en una clase llamada Crypto.cs que utiliza otra llamada CryptoProvider.cs que se encarga de inicializar el proveedor de encriptación. Hay que tener en cuenta que el algoritmo Rijndael se basa en dos cadenas: la clave y el vector de inicialización. Ambos valores se guardan en el app.config de la clase de negocio o se codifican en la clase. Al inicializar el proveedor Rijndael, ambos valores son pasados a su constructor para que pueda encriptar y desencriptar.

En este método, lo que se hace es abrir dos streams, uno para la entrada y otro para la salida. Seguidamente se inicializa el proveedor de servicios de encriptación con la clave (Key) y el vector de inicialización (IV), en modo encriptación. Una vez inicializado, el siguiente paso es crear un stream encriptado para ir pasando del fichero de origen al destino. Pasamos la información de un fichero a otro a través del CryptoStream, cerramos todos los flujos y eliminamos el fichero original.

Ya está encriptado, ¿cómo lo ve el usuario?
Ya tenemos el fichero encriptado en el servidor, por lo que el siguiente paso será desencriptarlo cuando haya que hacerlo sin dejar copia en el servidor. Para ello nos vamos a crear una página aspx que devuelva un flujo desencriptado al explorador del usuario.










En el momento que queramos descargar/visualizar el fichero desencriptado, la página en la que nos encontremos invocará a la página ObtenerPDF.aspx que solicitará el fichero desencriptado a la clase Crypto.cs. Esta clase, devolverá un MemoryStream desencriptado que será devuelto al explorador del cliente (junto con las cabeceras necesarias) para que sea renderizado en pantalla.















En mi caso, miro el fichero a desencriptar de la sesión del usuario. Habría que comprobar la seguridad para evitar accesos indebidos. Una vez obtenido el fichero, invocamos al método DesencriptarFichero() de la clase Crypto, que devuelve un objeto del tipo MemoryStream. Ese objeto es devuelto en el Response de la página para que el cliente lo muestre en pantalla.

El método DesencriptarFichero realiza las operaciones inversas que se realizaron en el método de encriptación.





















Con esto tenemos ya listo nuestro sistema de encriptación/desencriptación de ficheros en servidor. Tened en cuenta que esta solución es para entornos de hosting remoto en los que no tenemos capacidad de tocar el sistema de archivos. Si el servidor es nuestro, existen otras alternativas como la reubicación en carpetas fuera del directorio web, u otras.

Conclusión
Como siempre, espero que os haya interesado este post. En este caso, el código que he puesto es solamente el de las clases Crypto y CryptoProvider. Cada caso es particular así que cada uno adaptará la solución a su entorno. En este artículo he intentado dar unas guías para que podáis de una forma "fácil" incluir la encriptación en vuestros desarrollos asp.net.


8 comentarios:

Anónimo dijo...

Como muestras la imagen el el explorador del cliente?
Se que obtengo un memorystream del metodo desencriptar fichero, pero no se como referirme a la imagen desde la pagina.
No se me dan muy bien las aplicaciones web ;p
Gracias!

Manuel Cardenas Thorlund dijo...

Buenas,
la imagen se muestra poniendo en el Page_Load de la página el contentType al tipo de la imagen. En el ejemplo, el contentType está puesto a "application/pdf" porque lo que devuelvo es un fichero PDF. Los exploradores leen este parámetro y actúan en consecuencia. En el caso del PDF, arrancan el plugin del visualizador PDF y muestran el stream que les llega. Si quisiéramos mostrar una imagen tendríamos que cambiar el contentType y ponerlo, por ejemplo a "image/gif" para indicar al explorador del cliente que le estás devolviendo una imagen.

Otra alternativa es que utilices este código para referirte a imágenes:
MemoryStream memImg = objEncriptador.DesencriptarFichero(imagenADescargar);

System.Drawing.Image img = System.Drawing.Image.FromStream(memImg);
img.Save(Response.OutputStream, ImageFormat.Jpeg);
memImg.Close();
Response.OutputStream.Flush();
Response.End();

Con este código la imagen desencriptada se va al outputstream devuelto por la página.

Espero que te haya servido de ayuda.

Anónimo dijo...

Muchas gracias de verdad!!

Nunca habia pensado que pudiesen hacerse estas cosas!!

Gracias gracias ^^

Manuel Cardenas Thorlund dijo...

No hay de que, gracias a ti por leer nuestros artículos

Anónimo dijo...

Buen articulo

Anónimo dijo...

Gracias por la información. Podrías mejorar la calidad de las imágenes y subir los archivos que no estan disponible.


Saludos,

wxo dijo...

hola puedes pasarme las clases es que las imagenes son muy pequeñas y no las alcanzo a distinguir y las clases estan dadas de baja, te lo agradeceriaa demasiado buenisimo aporte... mi correo es luisr9a@hotmail.com

Dylan Arroyo Mejías dijo...

Est link esta dañado, no se puede descagar D:

me podes pasar el proyecto a este correo dylan_arroyomejias@hotmail.com Muchas gracias!