如何转义multipart/form-data下Content-Disposition头的name参数
multipart/form-data 表单字段name参数的转义规则
当使用HTTP/1.1协议以multipart/form-data内容类型提交表单时,每个分段都必须携带Content-Disposition头,通过name参数指定对应的表单字段名,基础格式示例如下:
--------------------------f0261bc90f5d4215 Content-Disposition: form-data; name="field name" <field_data>
官方规范定义的转义规则
你观察到的curl的处理逻辑完全符合现行标准规范:
multipart/form-data的现行规范为RFC 7578,其中明确要求Content-Disposition头的参数格式遵循RFC 6266(Content-Disposition头规范)的定义- RFC 6266进一步引用HTTP头字段值的通用语法规范:当参数值使用双引号包裹(即quoted-string格式)时,仅需要对两个字符做反斜杠转义:
- 双引号
"→ 转义为\" - 反斜杠
\→ 转义为\\
- 双引号
- 其余所有字节,包括UTF-8编码的非ASCII字符、空格、其他标点符号,都可以直接原样写入双引号包裹的参数值中,不需要额外转义。
注意:规范不建议在表单字段名中使用ASCII控制字符(0x00-0x1F、0x7F),这类字符本身不属于合法的表单字段名范畴,和转义规则无关。
curl行为验证
你做的抓包测试结果和规范完全匹配,测试逻辑如下:
$ nc -l 1234 | hexdump -C& $ curl -s -m1 http://localhost:1234 --form 'a"a=1' --form 'béb=2'
抓包得到的原始请求完整数据:
00000000 50 4f 53 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d |POST / HTTP/1.1.| 00000010 0a 48 6f 73 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 |.Host: localhost| 00000020 3a 31 32 33 34 0d 0a 55 73 65 72 2d 41 67 65 6e |:1234..User-Agen| 00000030 74 3a 20 63 75 72 6c 2f 37 2e 35 38 2e 30 0d 0a |t: curl/7.58.0..| 00000040 41 63 63 65 70 74 3a 20 2a 2f 2a 0d 0a 43 6f 6e |Accept: */*..Con| 00000050 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 32 33 34 |tent-Length: 234| 00000060 0d 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 |..Content-Type: | 00000070 6d 75 6c 74 69 70 61 72 74 2f 66 6f 72 6d 2d 64 |multipart/form-d| 00000080 61 74 61 3b 20 62 6f 75 6e 64 61 72 79 3d 2d 2d |ata; boundary=--| 00000090 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d |----------------| 000000a0 2d 2d 2d 2d 2d 2d 37 32 65 65 63 63 37 65 39 61 |------72eecc7e9a| 000000b0 65 65 66 30 31 37 0d 0a 0d 0a 2d 2d 2d 2d 2d 2d |eef017....------| 000000c0 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d |----------------| 000000d0 2d 2d 2d 2d 37 32 65 65 63 63 37 65 39 61 65 65 |----72eecc7e9aee| 000000e0 66 30 31 37 0d 0a 43 6f 6e 74 65 6e 74 2d 44 69 |f017..Content-Di| 000000f0 73 70 6f 73 69 74 69 6f 6e 3a 20 66 6f 72 6d 2d |sposition: form-| 00000100 64 61 74 61 3b 20 6e 61 6d 65 3d 22 61 5c 22 61 |data; name="a\"a| 00000110 22 0d 0a 0d 0a 31 0d 0a 2d 2d 2d 2d 2d 2d 2d 2d |"....1..--------| 00000120 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d |----------------| 00000130 2d 2d 37 32 65 65 63 63 37 65 39 61 65 65 66 30 |--72eecc7e9aeef0| 00000140 31 37 0d 0a 43 6f 6e 74 65 6e 74 2d 44 69 73 70 |17..Content-Disp| 00000150 6f 73 69 74 69 6f 6e 3a 20 66 6f 72 6d 2d 64 61 |osition: form-da| 00000160 74 61 3b 20 6e 61 6d 65 3d 22 62 c3 a9 62 22 0d |ta; name="béb".| 00000170 0a 0d 0a 32 0d 0a 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d |...2..----------| 00000180 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d 2d |----------------| 00000190 37 32 65 65 63 63 37 65 39 61 65 65 66 30 31 37 |72eecc7e9aeef017| 000001a0 2d 2d 0d 0a |--..| 000001a4
从抓包结果可以验证:
- 字段名
a"a中的双引号被转义为\" - 字段名
béb中的非ASCII字符é(UTF-8编码为0xc3 0xa9)直接透传,没有做任何转义处理,和规范要求完全一致。
内容的提问来源于stack exchange,提问作者Sylvain Leroux
相关产品推荐
相关产品推荐

