【问题标题】:file name scrubbing on the client side vs server side客户端与服务器端的文件名清理
【发布时间】:2017-10-24 17:09:43
【问题描述】:

当从多个单独的网页和控制器上传文件时,我的 C# 后端会在名称中包含 HTML 实体的文件被上传时中断:

An exception of type 'System.Web.HttpRequestValidationException' occurred in System.Web.Mvc.dll but was not handled in user code

Additional information: A potentially dangerous Request.Files value was detected from the client (filename="...for_files_#US2023103...").

请注意名称中的 HTML 实体 #,它应该只是 #。我可以浏览每个网页的 JS 并强制 JS 在客户端更改/解码 HTML 实体(很多小的更改),或者我可以更改服务器端代码来处理这个问题,但我不确定服务器端方法可以在不将服务器暴露于漏洞的情况下完成。

为了防止这是一个固执己见的问题,我明确要求:

有没有什么方法可以在服务器端转义/验证文件名,而不抛出异常,也不会使服务器暴露于漏洞?

这是我试图用来替换文件名的代码:

        if (requestContext.HttpContext.Request.Files.Count > 0)
        {
            foreach (HttpPostedFileBase file in requestContext.HttpContext.Request.Files)
            {
                file.FileName = System.Net.WebUtility.HtmlDecode(file.FileName);
            }
        }

这当然是不允许的,因为 FileName 是HttpPostedFileBase 的只读属性。我认为检查文件名可能很聪明,如果它有 HTML 实体或分号,然后用更正的名称实例化一个新的 HttpPostedFileBase 对象。

【问题讨论】:

  • 请向我们展示引发异常的代码
  • 刚刚结束线程标题,您应该始终在服务器端执行数据清理,即使您在客户端上有或没有它。你不能相信客户会做你期望它做的事情。服务器是数据存储或发布给该用户或其他用户之前的最后一道防线。
  • @JacobIRR Wndrr 的回答解决了您的问题?随意接受他的回答

标签: javascript c# jquery asp.net-mvc


【解决方案1】:

我不知道您使用的是哪个 .net 框架,但您可以尝试使用 HttpUtility.HtmlDecode 方法。

关于安全性的注意事项:清理/验证必须在服务器端进行。客户端用于用户体验。您在客户端所做的任何事情都不是真正安全的,因为如果不在服务器上重新执行,您无法确保它确实已完成(这是我对标题的咆哮)

你可以试试

if (requestContext.HttpContext.Request.Files.Count > 0)
{
    var finalDir = "path/to/dir"
    foreach (HttpPostedFileBase file in requestContext.HttpContext.Request.Files)
    {
        var decodedFileName = System.Net.WebUtility.HtmlDecode(file.FileName);

        try
        {
            // Will not overwrite if the destination file already exists.
            if(filename.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
                throw new Exception('invalid name');
            else
                File.Copy(decodedFileName , Path.Combine(finalDir, Guid.NewGuid()));
        }

        // Catch exception if the file was already copied.
        catch (IOException copyError)
        {
            Console.WriteLine(copyError.Message);
        }
    }
}

或者更简单,根据this SO post

if (requestContext.HttpContext.Request.Files.Count > 0)
{
    foreach (HttpPostedFileBase file in requestContext.HttpCont
    {
        file.SaveAs(Path.Combine("destination/path/", Guid.NewGuid());
    }
}

【讨论】:

  • 当请求第一次启动时,我用我在服务器端尝试的一些代码更新了 OP。
  • 为什么需要更新文件的名称属性?只需将解码后的文件名保存在另一个 var 中并使用此名称复制文件?
  • 我更新了我的答案,如果有帮助,请告诉我:-)
  • 您还可以检查文件名是否适用于 windows filename.IndexOfAny(Path.GetInvalidFileNameChars())。
  • 我用我认为正确使用您的建议的方法更新了我的答案@Fran 请检查它,如果我误解了应该如何使用它,请告诉我。我从来没有听说过这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-25
  • 2019-03-18
  • 1970-01-01
  • 2014-02-20
  • 2023-03-20
  • 2011-08-07
相关资源
最近更新 更多