ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Apache Arrow 安全模型全解:从列式格式到 IPC,如何安全地读取不可信数据

Apache Arrow 安全模型全解:从列式格式到 IPC,如何安全地读取不可信数据 Apache Arrow 安全模型全解从列式格式到 IPC如何安全地读取不可信数据【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow本文基于 Apache Arrow 官方的安全考量文档docs/source/format/Security.rst系统讲解当 Arrow 数据来自不可信来源untrusted sources时的完整安全模型列式格式中无效偏移与未初始化内存带来的风险、C Data Interface 的指针信任边界、IPC 格式的元数据与压缩炸弹攻击面以及扩展类型的反序列化钩子安全要求。读完本文你将掌握判断数据在哪个环节需要校验的方法并能在 C/IPC 场景下使用仓库中真实的验证 APIValidate/ValidateFull与 fuzzing 基础设施落实防护。文档定位与适用读者原始文档明确界定了自己的范围描述从不可信来源读取 Arrow 数据时的安全考量且聚焦于以标准化序列化形式如 IPC 流传递的数据而非某个实现私有的原生表示如 C 的arrow::Array。文档特别强调API 误用等实现层面的问题不在其讨论范围内应参考各实现自身的文档在 C 中即 docs/source/cpp/security.rst。文档要求两类读者必读Arrow 的使用者users第三方库或应用的开发者不直接实现 Arrow 格式与协议而是调用语言库提供的 APIArrow 库的实现者implementors提供屏蔽格式与协议细节之 API 的库包括官方各语言实现。这一使用者/实现者双视角贯穿全文也是后文每一节建议的组织方式。列式格式Columnar Format的安全模型无效数据偏移越界与非法 UTF-8列式格式见 docs/source/format/Columnar.rst是一种以性能与效率为优先的高效二进制表示。虽然格式本身不存储原始指针但 Arrow 缓冲区的内容在运行时经常会被组合、转换为指向进程地址空间的指针。因此无效的 Arrow 数据可能导致非法内存访问可能导致进程崩溃访问到非 Arrow 数据可能让攻击者窃取进程中的敏感信息。文档给出了一个极具代表性的例子。读取一个 Binary 数组元素需要两步从数组的 offsets 缓冲区读取该元素的偏移量在 data 缓冲区中读取由这对偏移量界定的字节区间。如果 offsets 是非法的无论故意构造还是意外产生第 2 步就会访问 data 缓冲区范围之外的内存。另一个层面的无效数据藏在值本身里。例如 String 数组按规定只能包含合法的 UTF-8 数据但不可信来源完全可能伪装成 String 数组发出非法 UTF-8。一个只针对合法 UTF-8 输入定义的算法比如查找字符边界时假设字节序列结构良好拿到这种输入后可能越界读取内存。好消息是只要已知 schemaArrow 数据可以在读取前预先校验validate up front从而保证后续读取不会再构成威胁。C 实现中这些校验 API 就定义在arrow::Array、arrow::RecordBatch、arrow::ChunkedArray、arrow::Table、arrow::Scalar上参见 cpp/src/arrow/array/array_base.h、cpp/src/arrow/record_batch.h、cpp/src/arrow/table.h 等头文件具体分两级结构校验Structural validityValidate方法。检查缓冲区数量、子数组数量等结构条件对行数近似常数时间、对子孙字段数线性适合捕获自己代码里的 bug但不足以防御所有恶意载荷完整校验Full validityValidateFull方法。在上面基础上检查所有变长类型的偏移量——这正是摄取不可信数据如来自 IPC 格式时根本性的检查防止变长偏移指向对应 data 缓冲区之外同时检查非法值如非法 UTF-8 字符串、超出宣称精度的十进制值详见 docs/source/cpp/security.rst 的 Data validity 一节。对使用者的建议Arrow 各实现为了高速处理通常假设输入符合规范因此文档**强烈建议extremely recommended**应用显式校验任何以序列化形式从不可信来源接收的 Arrow 数据许多实现都提供了专门的校验 API。对实现者的建议**建议recommended**提供专门的 API 来校验数组和/或记录批record batch让用户能够判断来自不可信来源的数据是否可以安全访问。一个典型的校验 API 必须满足数据非法时返回定义良好的错误而不是崩溃无论数据是否合法执行它都必须是安全的。未初始化数据一个更隐蔽的坑一个不那么明显的陷阱是数组的部分内容处于未初始化状态。例如当原始类型数组的某个元素被 validity bitmap 标记为 null 时其在 values 缓冲区中对应的值槽位value slot在任何用途上都可以被忽略。于是构造带 null 值的数组时很自然地就不去初始化这些值槽位。但这引入了一个严重的风险一旦该 Arrow 数据被序列化并对外发布如通过 IPC 或 Flight未初始化的值槽位可能泄露同一进程中先前内存分配遗留的数据——依应用不同其中可能包含敏感信息。对使用者与实现者的建议当数组可能被发送或读取给不可信第三方时建议recommended永远不要让缓冲区中的任何数据保持未初始化即使这些数据在逻辑上无关紧要。最简单的做法是对无法被完全填充的缓冲区做零初始化。如果经基准测试benchmarking确认零初始化带来过高的性能开销库或应用也可以内部使用未初始化内存作为优化但必须确保在把数据交给其他系统之前清空这些未初始化的值。文档还特别提醒了一个间接泄露场景Arrow 数据离开当前进程可能是间接发生的——例如你通过 C Data Interface 生产数据而消费方将其以 IPC 格式持久化到某个公共存储上。此时数据不会离开进程的假设即告失效。C Data Interface指针信任边界C Data Interface规格见 docs/source/format/CDataInterface.rst包含指向进程地址空间的原始指针。一般情况下你无法验证这些指针是否合法读取这样的指针可能崩溃也可能访问到无关的、甚至是虚假的数据。对使用者**绝不要never**从一个不可信的生产者处消费 C Data Interface 结构体——由于上述原因这种情况在构造上就无法防御危险行为。对实现者消费 C Data Interface 结构体时可以假设它来自可信生产者但仍建议recommended验证其健全性例如确认给定数据类型收到了正确数量的缓冲区因为可信生产者也可能有 bug。这条建议与 docs/source/cpp/security.rst 中指针有效性一节呼应C API 总是假设指针有效且指向所需大小的内存禁止传空指针除非 API 文档明确说明允许。IPC 格式列式风险之上的额外攻击面IPC 格式docs/source/format/IPC.rst是带有关联元数据的列式格式的序列化形式。从不可信来源读取 IPC 流或文件除了列式格式本身的注意事项外IPC 附带的信令与元数据引入自己的风险IPC 消息中编码的缓冲区偏移与大小可能超出 IPC 流的范围Flatbuffers 编码的元数据载荷可能携带指向指定元数据区之外的错误偏移。此外IPC 格式提供可选的缓冲区压缩buffer compression使用通用压缩算法。这意味着可以构造一个充当**解压炸弹decompression bomb**的 IPC 流或文件耗尽全部可用内存为拒绝服务DoS攻击打开通道。对使用者Arrow 库通常会保证 IPC 流是结构上合法的但可能不会进一步校验底层的 Array 数据。因此强烈建议extremely recommended使用相应 API 校验从不可信 IPC 流中读出的 Arrow 数据。这一说法与 C 实现完全一致IPC、Parquet、CSV 读取器是少数支持摄取不可信数据的 API但它们不会自动校验返回的数据——自动校验昂贵且在可信来源场景下没有必要所以使用这些 API 处理潜在非法数据时必须1) 检查 API 返回的错误2) 成功后对返回的 Arrow 数据做完整校验ValidateFull。对实现者强烈建议extremely recommended在解码 IPC 格式时执行专门的校验检查确保解码过程不会诱发意外行为校验失败时应向调用者返回知名的错误well-known error而非崩溃。**建议recommended**提供让用户控制读取 IPC 文件/流时内存分配行为的机制例如让分配器可定制。C 端对控制内存分配的落地方式就是MemoryPool接口你可以实现一个MemoryPool类强制任何限制例如限制总分配字节数并传给任意 Arrow C API。文档还注明与 Arrow 数据所用内存不同较小的元数据结构如字段名依赖 C 标准库分配器因此不会出现在 MemoryPool 的内存统计中见 docs/source/cpp/security.rst 中 Controlling and restricting memory allocation 一节的注记。Extension Types反序列化钩子是最脆弱的入口点扩展类型通常注册一个自定义的反序列化钩子deserialization hook以便从外部来源例如 IPC读取时能自动重建。该钩子需要从一个扩展类型专属的字符串或二进制载荷中解码其参数典型的例子如文档 docs/source/format/Columnar.rst 中的 opaque extension 示例使用一种定制的 JSON 表示以对象字段表达各个参数。从不可信来源读取数据时任何已注册的反序列化钩子都可能被任意载荷调用。因此钩子对无效乃至恶意的数据安全可调用这一点至关重要of primary importance。这要求必须使用对恶意数据健壮的元数据序列化格式——例如 JSON而不是Python 的pickle模块或 R 的serialize()这类可执行任意代码的序列化机制。对使用者与实现者设计扩展类型时**强烈建议extremely recommended**选择对潜在恶意数据健壮的元数据序列化格式实现扩展类型时建议recommended确保反序列化钩子在序列化的元数据载荷非法时能够检测并优雅地报错error out gracefully。健壮性测试回归文件与 Fuzzing对实现者对于可能处理不可信输入的 API**强烈建议extremely recommended**单元测试用典型的非法数据来攻击你的 API。例如校验 API 必须针对非法 Binary/List 偏移、String 数组中的非法 UTF-8 等场景进行测试。基于已知回归文件的测试Apache 的 arrow-testing 仓库包含各种格式包括 IPC 格式的回归文件。其中两类文件尤为值得注意可以用来考验一个 Arrow 实现的健壮性黄金集成文件gold integration files合法文件用于检验对 Arrow IPC 特性的合规性fuzz 回归文件fuzz regression files每次 fuzz 器发现由特定通常非法输入触发的 bug 时自动生成。Fuzzing 实战仓库中的真实实现文档建议更进一步建立针对未预见输入的自动化健壮性测试典型手段是 fuzzing可结合能检测危险行为的运行时插桩框架如 C 或 Rust 中的 AddressSanitizer。文档给出的合理的 Arrow fuzzing 设置方式是以IPC 格式作为二进制载荷fuzz target 不仅要尝试把 IPC 流解码为 Arrow 数据还应随后校验validate这份 Arrow 数据——这能同时强化 IPC 解码器与校验例程抵御非法、潜在恶意数据的能力最后若校验成功fuzz target 可演练一些核心功能如打印数据供人工查看以确认校验例程没有放过去后续会导致危险行为的无效数据。当前仓库的 C 实现完整落地了这一建议IPC 流的 fuzz target 位于 cpp/src/arrow/ipc/stream_fuzz.cc其LLVMFuzzerTestOneInput第 25-28 行直接把输入交给arrow::ipc::internal::FuzzIpcStream解码并用LogFuzzStatus记录结果另有文件级 cpp/src/arrow/ipc/file_fuzz.cc、张量流 cpp/src/arrow/ipc/tensor_stream_fuzz.cc以及 CSV 的 cpp/src/arrow/csv/fuzz.cc解码用的语料生成器见 cpp/src/arrow/ipc/generate_fuzz_corpus.cc 等针对上文提到的解压炸弹/内存耗尽问题cpp/src/arrow/util/fuzz_internal.h 第 29 行定义了kFuzzingMemoryLimit2200 MB略低于 OSS-Fuzz 的 2560 MB 默认 RSS 上限目的是在进程被杀之前主动让分配失败并暴露fuzzing_memory_pool()返回一个超限即拒绝分配的内存池——这正是提供设施让用户控制内存分配行为这条建议在工程上的具体体现校验 API 本体Validate/ValidateFull定义于 cpp/src/arrow/array/array_base.h 与 cpp/src/arrow/scalar.h、cpp/src/arrow/chunked_array.h 等头文件其安全语义unsafe 变体如UnsafeAppend跳过检查、由调用方保证前置条件在 docs/source/cpp/security.rst 的 Safe and unsafe APIs 一节有系统说明。非 Arrow 格式与协议的边界Arrow 数据也可以通过第三方格式如 Apache Parquet发送或存储。这些格式是否具有与上文相同的安全风险并不确定——例如Parquet 这类不为 null 元素创建值槽位的格式就不存在未初始化数据那条注意事项。文档建议读者参考这些项目自己的文档获取更具体的指导。小结一张风险—防线对照表数据入口主要风险核心防线仓库证据列式格式序列化形式非法 offsets 越界读、非法 UTF-8 触发未定义行为读前预校验ValidateFulldocs/source/cpp/security.rst未初始化值槽位序列化发布后泄露进程内存残留零初始化缓冲区间接出口C Data Interface → IPC 持久化同样算离开进程C Data Interface原始指针无法验证合法性永不消费不可信生产者的结构实现者仍应校验结构健全性IPC 流/文件缓冲区偏移越界、Flatbuffers 元数据偏移错误、压缩炸弹 DoS解码时专门校验并返回已知错误MemoryPool限流cpp/src/arrow/util/fuzz_internal.h 的 fuzzing 内存池即范例Extension Types 反序列化钩子任意载荷触发钩子用 JSON 等健壮格式承载元数据钩子对非法载荷优雅报错全部不可信输入路径未知非法输入单元测试覆盖典型非法数据回归文件fuzzingcpp/src/arrow/ipc/stream_fuzz.cc适用前提与限制以上安全模型针对跨边界读取不可信数据的场景。数据始终留在可信进程内、以原生对象如arrow::Array形式传递时问题性质转变为 API 使用正确性应参考各语言实现自身的文档如 docs/source/cpp/security.rst而非本文所讨论的格式层防线。【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表