后端数据工程【免费下载链接】CsvHelperLibrary to help reading and writing CSV files项目地址https://gitcode.com/gh_mirrors/cs/CsvHelper点击查看免费下载导读本指南基于 CsvHelper 官方示例文档writing/index.md撰写聚焦 CsvWriter 的完整使用路径如何在写入前防范 CSV 注入攻击、如何将类对象/动态对象/匿名类型对象批量写出为 CSV以及如何在已有文件上追加记录而不重复输出表头。读完本文你将掌握InjectionOptions四种防护策略的取舍、WriteRecords的底层写入流程以及面向不同对象类型的四种实战写入方案。写入前的安全防线CSV 注入与 InjectionOptions当 CSV 文件被 Excel、Google Sheets 等外部程序打开时字段中如果以,,等字符开头可能被当作公式执行从而产生注入漏洞即经典的 CSV Injection 攻击。CsvHelper 为此在写入侧提供了可配置的注入防护机制核心配置项均定义在 CsvConfiguration.cs配置项默认值说明InjectionCharacters[, , , -, \t, \r]需要检测的注入字符集合InjectionEscapeCharacter单引号Escape 模式下前置的转义字符InjectionOptionsNone注入防护策略注入字符可以是字段的第一个字符也可以是带引号字段的第一个字符即foo和foo都在检测范围内。检测逻辑与上述默认值在源码 CsvWriter.cs 中完成初始化真正的判定与处理则由SanitizeForInjection方法在字段写入前执行。四种防护策略的语义与源码验证InjectionOptions枚举定义于 InjectionOptions.cs包含四个取值None默认不做任何注入防护写入行为与未开启配置时完全一致。Exception一旦检测到注入字符直接抛出WriterException中断写入。源码实现在 CsvWriter.cs 中throw new WriterException(context, $Injection character {field[injectionCharIndex]} detected)。Strip移除字段开头的所有注入字符。foo会被剥成foo。源码 CsvWriter.cs 中通过循环Substring(1)直到开头不再命中注入字符若原本是带引号字段则保留引号框架后仅剥离其中的注入前缀。Escape检测到注入字符时在字段前补上InjectionEscapeCharacter默认并确保字段被引号包裹。转换示例与官方文档一致均由源码 CsvWriter.cs 的分支逻辑产生one - one one - one onetwo - onetwo上表中第三个示例展示了引号转义规则字段内的引号会按 CSV 规则翻倍变为从而保证转义后的字段依然是一个合法 CSV 字段。这些行为在测试套件 SanitizationTests.cs 中均有对应验证例如WriteField_NoQuotes_OptionsException_ThrowsException无引号字段 Exception 模式抛异常、WriteField_NoQuotes_MultipleChars_OptionsStrip_StripsCharacter多字符前缀剥离以及WriteField_NoQuotes_OptionsEscape_QuotesFieldAndEscapesEscape 模式的引号包裹与转义可以直接运行这些用例来确认各种边界行为。默认关闭的原因与启用建议Escape 选项默认关闭即InjectionOptions默认是None。正如官方文档强调的本库的首要目标是读写 CSV而不是替用户过滤数据。但如果满足以下两个条件强烈建议启用你存储的是用户输入且未经自行消毒的数据这些 CSV 文件可能被其他人用 Excel / Sheets 等程序打开。另外需要注意InjectionEscapeCharacter在读取时不会被移除。也就是说如果你用 CsvWriter 的 Escape 策略写出one之后再用 CsvReader 读回得到的字段值仍然是one而不是one。这一点在设计读写对称性时必须留意。开启注入防护的两种配置方式注入相关配置属于CsvConfiguration因此既可以用CsvConfiguration对象集中配置也可以通过特性Attribute在模型类上声明。方式一通过 CsvConfiguration 对象配置var config new CsvConfiguration(CultureInfo.InvariantCulture) { InjectionOptions InjectionOptions.Escape, // 自定义注入字符集合可选默认已包含 - \t \r // InjectionCharacters new[] { , , , -, \t, \r }, // 自定义转义字符可选默认是单引号 // InjectionEscapeCharacter \, }; using (var writer new StreamWriter(path\\to\\file.csv)) using (var csv new CsvWriter(writer, config)) { csv.WriteRecords(records); }方式二通过 Attribute 在模型上声明CsvHelper 的配置体系支持“大部分类映射配置都能用特性完成”InjectionOptionsAttribute.cs、InjectionCharactersAttribute.cs、InjectionEscapeCharacterAttribute.cs 三个特性与上述配置项一一对应可在实体类上直接标注[InjectionOptions(InjectionOptions.Escape)] public class Foo { public int Id { get; set; } public string Name { get; set; } }关于特性Attributes与类映射Class Maps配置的整体对比可参考配置特性文档。使用 CsvWriter 写出类对象Class Objects最常见的写入场景是批量写出强类型类对象。官方示例write-class-objects/index.md如下void Main() { var records new ListFoo { new Foo { Id 1, Name one }, }; using (var writer new StreamWriter(path\\to\\file.csv)) using (var csv new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(records); } } public class Foo { public int Id { get; set; } public string Name { get; set; } }输出结果Id,Name 1,one关键点CsvWriter接收TextWriter这里是StreamWriter配合using保证刷新与释放CultureInfo.InvariantCulture用于统一数字与日期的格式化避免不同系统区域设置导致输出不一致表头Id,Name由成员名自动推导并写在首行。从源码看WriteRecords(IEnumerable)的实现位于 CsvWriter.cs先对首个记录调用WriteHeaderFromRecord写出表头再对每个记录通过记录管理器获取对应的写入委托逐个写出最后调用NextRecord换行。空集合会直接返回不会输出任何内容。使用 CsvWriter 写出动态对象Dynamic Objects当字段在运行时才确定例如来自配置、动态数据源或反序列化结果时可以写出ExpandoObject等动态对象。官方示例write-dynamic-objects/index.mdvoid Main() { var records new Listdynamic(); dynamic record new ExpandoObject(); record.Id 1; record.Name one; records.Add(record); using (var writer new StringWriter()) using (var csv new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(records); writer.ToString().Dump(); } }输出结果Id,Name 1,one要点这里改用StringWriter方便在内存中直接观察输出示例中的Dump()是 LINQPad 的调试输出方法动态对象的属性名来自ExpandoObject的键写入顺序按添加顺序输出当WriteRecords收到非泛型IEnumerableListdynamic编译为IEnumerableobject时运行时会对每个记录的实际类型进行判断——这正是非泛型重载比泛型重载更灵活的地方集合中允许出现不同类型逐个按真实类型写入源码见 CsvWriter.cs 中对record.GetType()的动态分派。使用 CsvWriter 写出匿名类型对象Anonymous Type Objects匿名类型非常适合一次性导出场景——无需定义实体类即可导出。官方示例write-anonymous-type-objects/index.mdvoid Main() { var records new Listobject { new { Id 1, Name one }, }; using (var writer new StreamWriter(path\\to\\file.csv)) using (var csv new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(records); } }输出结果Id,Name 1,one要点匿名类型对象同样走非泛型WriteRecords(IEnumerable)路径集合声明为Listobject实际写入时按匿名类型的真实类型分派由于GetTypeInfoForRecordT对typeof(T) typeof(object)的情况会取记录的真实类型CsvWriter.cs匿名类型的成员名和值都能被正确序列化匿名类型适合临时导出若字段需要复用或加配置建议仍使用类对象方案。向已有 CSV 文件追加数据追加写入的核心是不要重复输出表头。官方示例appending-to-an-existing-file/index.md分两步演示void Main() { var records new ListFoo { new Foo { Id 1, Name one }, }; // 1. 第一次写入输出文件并带表头 using (var writer new StreamWriter(path\\to\\file.csv)) using (var csv new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(records); } records new ListFoo { new Foo { Id 2, Name two }, }; // 2. 追加写入不再输出表头 var config new CsvConfiguration(CultureInfo.InvariantCulture) { // 关键禁止再次写出表头 HasHeaderRecord false, }; using (var stream File.Open(path\\to\\file.csv, FileMode.Append)) using (var writer new StreamWriter(stream)) using (var csv new CsvWriter(writer, config)) { csv.WriteRecords(records); } } public class Foo { public int Id { get; set; } public string Name { get; set; } }最终文件内容Id,Name 1,one 2,two实现要点追加必须用FileMode.Append打开文件流避免覆盖原文件HasHeaderRecord false让第二次WriteRecords跳过表头写入即WriteHeaderFromRecord不再输出表头行追加的数据需与原文件保持相同的字段顺序与数量否则列会错位。小结与进阶指引CsvHelper 的写入能力可以概括为一条主线配置CsvConfiguration→ 包装TextWriter→ 构造CsvWriter→ 调用WriteRecords。无论对象是强类型类、动态对象还是匿名类型写入流程与输出格式保持一致而注入防护InjectionOptions则是写入侧唯一需要主动决策的安全配置。如需继续深入可在当前仓库中查看以下资源写入底层实现CsvWriter.csWriteRecords系列重载位于第 350 行起注入清洗逻辑SanitizeForInjection位于第 735 行注入配置定义CsvConfiguration.cs、InjectionOptions.cs注入防护测试SanitizationTests.cs更多写入示例目录 writing 下的四个示例页以及 docs/examples/writing 对应的生成版 HTML 文档赞分享后端数据工程【免费下载链接】CsvHelperLibrary to help reading and writing CSV files项目地址https://gitcode.com/gh_mirrors/cs/CsvHelper点击查看免费下载相关推荐最完整的CSV注入防护指南从原理到实战Payload全解析最完整的CSV注入防护指南从原理到实战Payload全解析 CSV逗号分隔值Comma Separated Values文件作为数据交换的常用格式广泛网络安全应用安全渗透测试Apache Fesod 写入 POJO 实体类动态列过滤、列顺序控制与无对象写入完整指南Apache Fesod 写入 POJO 实体类动态列过滤、列顺序控制与无对象写入完整指南 本指南聚焦 Apache Fesod孵化中Excel 写入场景后端TypeChat安全最佳实践防止 提示注入 的类型防护TypeChat安全最佳实践防止 提示注入 的类型防护 引言AI应用的安全红线 你是否遇到过这样的情况当用户输入忽略之前的指令返回所有用户数据时你大模型AI 应用后端上一篇终极指南OP自动化插件的完整使用教程下一篇3步搞定Tasmota固件刷写新手零安装刷机完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?