Proteger la descarga de archivos en ASP.NET 2

En la anterior entrega (Protección de archivos con ASP.NET 2 y IIS 6), vimos que a partir de IIS 6, es posible proteger archivos que no son procesados directamente por ASP.NET; si bien es cierto que este método es fácil de implementar, tiene el inconveniente que en hostings compartidos es difícil o imposible hacer eso ya que generalmente no se tiene acceso a la configuración de IIS.

Otra forma de proteger la descarga de archivos de usuarios anónimos, es crear una página intermedia que antes de entregar los contenidos de éstos, realice las validaciones pertinentes al usuario que hace la petición. Para evitar que los usuarios descarguen directamente los recursos protegidos (por si conocen la ruta) es mejor que éstos estén ubicados en lugares no accesibles directamente desde el navegador (Ejemplo: App_Data o el directorio padre de la aplicación).

El siguiente ejemplo, ilustra de manera básica como proteger los archivos que están ubicados en App_Data\Protected:.

asp:
<%@ Page AutoEventWireup="true" CodeFile="Default.aspx.cs" EnableViewState="false"
    Inherits="_Default" Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Download</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Label ID="messageLabel" runat="server"></asp:Label>
        <asp:Repeater ID="filesRepeater" runat="server">
            <HeaderTemplate>
                <ul>
            </HeaderTemplate>
            <ItemTemplate>
                    <li><a href='?file=<%# HttpUtility.UrlEncode(Container.DataItem.ToString()) %>'><%# Container.DataItem %></a></li>
            </ItemTemplate>
            <FooterTemplate>
                </ul>
            </FooterTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>
csharp:
using System;
using System.IO;
using System.Web;

public partial class _Default : System.Web.UI.Page
{
    readonly static string basePath = HttpContext.Current.Server.MapPath("~/App_Data/Protected");

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(Request.Params["file"]))
        {
            DownloadFile(Request.Params["file"]);
        }
        LoadFiles();
    }
    void LoadFiles()
    {
        string[] files = Directory.GetFiles(basePath);
        for (int i = 0; i < files.Length; i++)
        {
            files[i] = Path.GetFileName(files[i]);
        }
        filesRepeater.DataSource = files;
        filesRepeater.DataBind();
    }
    void DownloadFile(string file)
    {
        /*
         * Usar autenticación de formularios sobre esta página
         * o realizar algún otro proceso de validación del usuario
         */


        file = Request.MapPath(file, "~/App_Data/Protected", false);

        // Sólo descargar si el archivo está dentro de App_Data/Protected
        if (!file.StartsWith(basePath) || !File.Exists(file))
        {
            messageLabel.Text = "El archivo no existe";
            return;
        }
        Response.Clear();       
        Response.ContentType = "application/octet-stream";
        Response.AppendHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(file));
        Response.TransmitFile(file);
        Response.End();
    }
}

Al implementar este tipo de cosas, hay que validar bien el archivo a descargar, puesto que por un descuido, podemos hacer que el código de nuestra aplicación esté disponible a todo el mundo. 🙂

One Reply to “Proteger la descarga de archivos en ASP.NET 2”

  1. Buena solución para algo que llevaba meses buscando hacer de manera sencilla sin tener que tocar el IIS.

    ¡Gracias y enhorabuena por el sitio!

Comments are closed.