-1

所以我需要创建一个文件验证器来检查文件类型是否正确。最初我们只是检查请求的内容类型,但与往常一样,我们的测试人员通过简单地将 exe 文件的文件扩展名更改为 .csv 来绕过限制,这可能会欺骗我们的直接检查。

这就是我目前在验证器中所拥有的

private bool IsCorrectFileType(IFormFile file)
        {
            using var reader = new StreamReader(file.OpenReadStream());
            using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

            try
            {
                csv.Read();
                csv.ReadHeader();
                List<string> headers = csv.Context.HeaderRecord.ToList();
            }
            catch (Exception _)
            {
                return false;
            }

            return true;
        }

我打算做的是,如果 CSV 阅读器在文件中找不到标题,那么我希望它会爆炸并返回 false,但正在发生的事情是文件的全部内容正在作为单个标题读入所有非 csv 文件类型的情况。导致它认为它确实是一个有效的 csv 文件并返回 true。

我一生都无法找到一种方法来捕捉 CSV 文件是否确实有效,因为在大多数情况下,CSV 阅读器可以将所有流作为字节数据读取,并且标题记录的上下文在此看起来像一个有效的 CSV案子。

令人讨厌的是,尽管我们永远不会上传带有单个标头的文件,但仅对标头进行标准计数以查看它是否只有一个标头来捕获此问题感觉很愚蠢。

任何帮助都将不胜感激,因为我对此感到有些困惑。在此先感谢,克里斯。

4

2 回答 2

1

我就是这样做的。

  1. 检查文件中是否存在 0x00 的任何字节。这些往往在二进制文件中很常见,但在文本文件中是不允许的,除非可能在最后作为空终止符。所以这可能是一个相对快速的健全性检查。

  2. 将文件分成几行(例如,按行分隔符\n和分割\r),然后检查每一行以确保它具有相同数量的逗号。请注意,某些列中可能包含逗号,您不能将其计算在内;包含嵌入逗号的列将用引号括起来以转义它们。因此,您编写了一些代码来解析行来进行计数。

  3. 如果上述两个步骤都通过,则文件仍然可能无效,例如,如果它包含无效的 UTF 序列。如果您想检查这些,请参阅这篇文章。

  4. 如果您知道文件中应该包含的内容,请使用正则表达式来验证每一行和每一列,以查看文件是否整体有效。

您可以只实施上面的步骤 1,或 1 和 2,或全部实施,具体取决于这有多重要。

于 2020-05-13T23:28:38.010 回答
0

在尝试了非 csv 类型的字符串在 csv 解析器标头上下文中的样子之后,我能够断言它是否只是乱码,即 exe 内容或 jpg 等等,它会在长字符串中包含非 ascii 字符。

下面的代码显示了我为检查是否是这种情况所做的工作。如果是,它会拒绝它,如果不是,它允许它被摄取。

/// <summary>
/// Minimises chances of incorrect file types being passed to the service that have been
/// maliciously changed to a csv format when the original is for example .exe .jpg and so on.
/// </summary>
/// <remarks>
/// The function below checks if a header row exists in the incoming file. In all cases where the CsvReader is
/// able to read the file it will either create a list of headers if the file is valid or subsequently if the file
/// uploaded has been modifed to look like a csv file the Context.HeaderRecord will read in all of the content to a
/// single header. If there is only one header in the file to make sure the file is valid I an running a string function
/// on the header to make sure it definitely includes ascii charachters if not in the case of any file thats malliciously
/// been changed it will load all of the bytes into the headerRecord which means it will fail the chack and fail validation.
/// This will in turn minimise the chances of a malicious file thats had its name changed name changed from hitting the file processor.
/// </remarks>
private bool IsCsvFileFormat(IFormFile file)
{
      using var reader = new StreamReader(file.OpenReadStream());
      using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);

      try
      {
           csv.Read();
           csv.ReadHeader();
           var headerRecordList = csv.Context.HeaderRecord.ToList();

           if (headerRecordList.Count() == 1)
               return !HasNonASCIIChars(headerRecordList.ElementAt(0));
       }
       catch (Exception _)
       {
            return false;
       }

       return true;
}

private bool HasNonASCIIChars(string str) =>
     (System.Text.Encoding.UTF8.GetByteCount(str) != str.Length);
于 2020-05-13T23:37:59.017 回答