使用基本身份验证进行安全文件下载






4.50/5 (16投票s)
2006年3月19日
6分钟阅读

130268

1631
使用基本身份验证安全下载文件。有趣的是,我们为文件的上传和下载维护了两个独立的入口点。
引言
安全一直是所有类型应用程序(尤其是Web应用程序)的首要问题。Web应用程序几乎对整个世界开放,并容易受到攻击。大多数Web应用程序都提供文件下载功能,真正的挑战不在于提供此功能,而在于保护此类操作。最近,我处理了一个需要安全文件上传和下载的应用程序,在此期间我进行了广泛的研究,所以我想与世界分享它,使其有价值。
你可能会在互联网上找到几篇关于安全下载的文章,但本文与所有这些文章略有不同,因为它通过为上传和下载提供两个不同的入口点来区分它们。
以下文章描述了如何使用IIS基本身份验证实现安全文件下载。
背景
在我们开始之前,让我们回顾一下并学习一些基础知识。
ASP.NET应用程序有两个独立的身份验证层。这是因为ASP.NET不是一个独立的产品。相反,它是IIS之上的一层。所有请求在交给ASP.NET之前都流经IIS。因此,IIS可以决定拒绝访问,而ASP.NET进程甚至不知道有人请求了特定的页面。以下是IIS和ASP.NET联合身份验证过程的步骤概述:
- IIS首先检查传入请求是否来自允许访问该域的IP地址。如果不是,则拒绝请求。
- 接下来,如果IIS配置为这样做,它会执行自己的用户身份验证。默认情况下,IIS允许匿名访问,因此请求会自动进行身份验证,但你可以在IIS中根据每个应用程序更改此默认设置。
- 如果请求与经过身份验证的用户一起传递给ASP.NET,ASP.NET会检查是否启用了模拟。如果启用了模拟,ASP.NET会像经过身份验证的用户一样行事。如果不是,ASP.NET会使用其自己的配置帐户行事。
- 最后,步骤3中的身份用于从操作系统请求资源。如果ASP.NET身份验证可以获取所有必要的资源,它会授予用户的请求,否则会拒绝。资源可以包括的不仅仅是ASP.NET页面本身。你还可以使用.NET的代码访问安全功能将此授权步骤扩展到磁盘文件、注册表项和其他资源。
工作原理
通常,任何Web应用程序都包含一个虚拟目录和一个“上传”文件夹,最终用户从/向其中上传/下载文件。在这里,我玩了一个小把戏:尽管上传和下载发生在同一个文件夹中,但我为它们保留了两个不同的入口点。也就是说,我创建了两个不同的虚拟目录,它们指向同一个物理文件夹。
- 在ASP.NET应用程序所在的Security(你的Web应用程序)虚拟目录中创建一个名为Security的虚拟目录。
- 在此文件夹中,创建一个名为“Uploads”(你的应用程序子文件夹,你将文件上传到此文件夹)的文件夹。ASP.NET应用程序将使用通用的文件上传实践将文件上传到此文件夹。
- 再创建一个名为“Downloads”的虚拟目录,并将其映射到位于“Security”(你的Web应用程序)文件夹中的“Uploads”(你的应用程序子文件夹,你将文件上传到此文件夹)文件夹。此虚拟目录仅用于文件下载,与ASP.NET应用程序或文件上传过程无关。
保持上传文件夹不变,我们为文件创建了两个入口点,一个是通过https:///security/uploads,另一个是通过https:///downloads。尽管这两个虚拟目录都指向同一个文件夹,但根据它们的设置,它们的行为会不同。
配置
- 将附加的源代码解压缩到磁盘上。
- 启动IIS。
- 创建一个名为“Security”的新虚拟目录,并将其映射到源代码。
- 在IIS中,选择“Security”虚拟目录(上面创建的)。你会在其中找到一个“Uploads”文件夹。右键单击“Uploads”文件夹并选择“属性”。删除“Uploads”文件夹的“读取”访问权限,并仅提供“写入”访问权限。
- 创建一个名为“Downloads”的新虚拟目录,并将“Security”文件夹中存在的“Uploads”文件夹映射到它。
- 打开downloads虚拟目录属性窗口,并仅授予读取权限,如下图所示:
使用代码
根据你的应用程序需求修改web.config文件中的以下参数
//Application uploads folder virtual path
<add key="UploadPath" value="/Security/Uploads" />
//Entry point for downloads folder, a virtual path
<add key="DownloadURL" value="https:///downloads" />
//Windows user name
<add key="BasicAuthenticationUser" value="administrator" />
//Windows user password
<add key="BasicAuthenticationPWD" value="admin$123" />
现在,让我们开始学习...................
什么是基本身份验证 - 当未经验证的请求进入Web服务器时,Web服务器返回HTTP 401响应,提示客户端提供其凭据。客户端重新请求同一资源,并在Base-64编码的HTTP头中传递用户名和密码。(Base-64编码不会加密或保护凭据;它只是确保通过线路发送的字符的格式不会与任何保留字符冲突。)由于凭据以明文形式通过线路发送,因此基本身份验证应仅在使用SSL时使用,因为这确保了HTTP请求的整个内容都已加密。但是,在我们的例子中,我们通过localhost将凭据传递给同一服务器,因此以明文形式传递凭据不会成为问题。
.NET Framework 提供了 `WebClient` 类,该类旨在简化 HTTP 请求过程。它包含用于向 URI 标识的资源发送数据和从该资源接收数据的常用方法。`HttpWebRequest` 类用于生成请求,`HttpWebResponse` 用于从服务器检索响应。通常,`HttpWebRequest` 和 `HttpWebResponse` 可以满足所有目的,但在基本身份验证的情况下,还需要一个额外的类,即 `CredentialCache`。
`WebClient` 和 `HttpWebRequest` 类都可以通过其 `Credentials` 属性轻松地在请求中包含身份验证信息。`Credentials` 属性接受实现 `ICredentials` 的对象。`CredentialCache` 类提供凭据存储。`CredentialCache` 类的目的是存储用户的凭据集。当对资源发出请求时,可以查询 `CredentialCache` 类,并根据请求的资源提取适当的凭据。
此类的最简单用法仅涉及几行代码。我们需要执行的步骤包括:
- 创建该类的实例。
- 调用`DownloadData`方法,传入URL(它返回一个`Byte`数组)。
- 使用`Response.BinaryWrite`写入下载数据的`Byte`,这反过来会提示用户下载文件。
以下代码展示了如何使用`CredentialCache`类和`WebClient`的`Credentials`属性向受基本身份验证保护的URL发出请求
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Net;
using System.IO;
namespace security
{
/// <SUMMARY>
/// Summary description for SecureFile.
/// </SUMMARY>
public class SecureFile
{
public SecureFile()
{
//
// TODO: Add constructor logic here
//
}
public bool UploadFile(HtmlInputFile inputfile)
{
try
{
string fileName = "";
string DirPath = "";
if(( inputfile.PostedFile != null ) &&
( inputfile.PostedFile.ContentLength > 0 ))
{
DirPath=HttpContext.Current.Server.MapPath(
System.Configuration.ConfigurationSettings.AppSettings[
"UploadPath"]);
fileName = System.IO.Path.GetFileName(
inputfile.PostedFile.FileName );
inputfile.PostedFile.SaveAs( DirPath + "\\" + fileName );
}
return true;
}
catch
{
return false;
}
}
public bool DownloadFile(string strFile)
{
try
{
string strDownloadURL=
System.Configuration.ConfigurationSettings.AppSettings[
"DownloadURL"];
string strUser=
System.Configuration.ConfigurationSettings.AppSettings[
"BasicAuthenticationUser"];
string strPWD=
System.Configuration.ConfigurationSettings.AppSettings[
"BasicAuthenticationPWD"];
string strURL=strDownloadURL + "\\" + strFile;
//Creating an instance of a WebClient
WebClient req=new WebClient();
//Creating an instance of a credential cache,
//and passing the username and password to it
CredentialCache mycache=new CredentialCache();
mycache.Add(new Uri(strURL),"Basic",
new NetworkCredential(strUser,strPWD));
req.Credentials=mycache;
//Creating an instance of a Response object
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.ClearContent();
response.ClearHeaders();
response.Buffer= true;
//Keep the current page as it is, and writes
//the content to an new instance,
//which prompts the user to download the file
response.AddHeader("Content-Disposition",
"attachment;filename=\"" + strFile + "\"");
byte[] data=req.DownloadData(strURL);
response.BinaryWrite(data);
response.End();
return true;
}
catch(Exception ex)
{
if(ex.Message=="The remote server " +
"returned an error: (404) Not Found.")
throw new Exception("File not found");
else if(ex.Message=="The remote server" +
" returned an error: (401) Unauthorized.")
throw new Exception("Unauthorized access");
return false;
}
}
}
}
尽情享受!!!任何反馈都将不胜感激。
使用代码
为了快速实现和演示目的,我使用了管理员用户,但我强烈建议你创建一个新用户,该用户仅对“Uploads”文件夹具有“写入”权限,并且在服务器/系统上没有其他权限。