在URL中传递Base64编码的字符串

322

通过GET参数传递原始的base64编码字符串是否安全?

11个回答

337

还有其他的base64规范。(具体请参见此处的表格)但基本上您需要65个字符来进行编码:26个小写字母+26个大写字母+10个数字=62。

您需要两个额外的['+', '/']以及一个填充字符'='。但它们都不适合URL,因此只需为它们使用不同的字符即可解决问题。从上面的表格中选择的标准字符是['-', '_'],但只要您解码它们的方式相同,并且不需要与他人共享,则可以使用其他字符。

我建议您编写自己的辅助程序。例如php手册页面上的评论中提供的这些帮助程序:

function base64_url_encode($input) {
 return strtr(base64_encode($input), '+/=', '._-');
}

function base64_url_decode($input) {
 return base64_decode(strtr($input, '._-', '+/='));
}

57
很好的解决方案,唯一问题是URL中逗号未被保留。我建议使用"~"(波浪号)或"."(点)代替。 - kralyk
16
我建议直接使用 urlencode,就像 rodrigo-silveira 的回答建议的那样。创建两个新函数只是为了在 URL 长度中节省一些字符,这就像从窗户进入你的房子,而不是使用门一样。 - Marco Demaio
8
不知道使用方式的情况下,无法确定这只是几个字符。每个编码字符将有三倍长度,为什么“+++…”不能是有效的base64字符串呢?URL具有浏览器限制,将URL乘以三可能会使您达到这些限制。 - leewz
11
根据RFC3986规范,波浪号(tilde)是安全的URL字符,属于未经保留的字符(unreserved)。未经保留的字符包括字母、数字、连字符、句点、下划线和波浪号。 - kralyk
5
由于“,”应该进行URL编码为“%2C”,我建议使用“.-”代替“-,”,就像https://en.wikipedia.org/wiki/Base64#Variants_summary_table中仅有的变体一样,它保留了尾随的=。 - PaulH
显示剩余4条评论

263
不需要,您需要对其进行URL编码,因为base64字符串可能包含"+"、"="和"/"这些字符,这些字符可能会改变您数据的含义 - 看起来像子文件夹。
下面是有效的base64字符。
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=

6
URL编码浪费了空间,特别是因为base64本身留下了许多未使用的字符。 - Michał Górny
35
我不确定我理解你的意思- URL编码不会改变除上述列表中的最后三个字符以外的任何字符,这是为了防止它们在URL中被解释错误,因为它们在URL中有其他含义。对于base64也是如此,原始数据可以是二进制或其他格式,但是它被编码成一种可使用简单协议轻松传输的形式。 - Thiyagaraj
5
首先,你应该同样转义'+'号,因为它可能会被转换为空格。其次,至少有一些字符在URL中使用是安全的,而且不在“标准”字符集中使用。在某些情况下,你的方法甚至会将传输数据大小增加三倍;而用其他字符替换这些字符可以达到相同的效果,同时保持相同的长度。这也是一个非常标准的解决方案。 - Michał Górny
16
http://en.wikipedia.org/wiki/Base64#URL_applications - 明确指出转义会使字符串变得不必要地更长,并提到了可替换字符集变量。 - Michał Górny
4
如果您将JSON用作GET参数,Base64编码可能会(根据您的数据)减小请求字符串的大小。对于我们的应用程序,我们在查询字符串中使用JSON以便深链接到我们的应用程序中,这不是一个愚蠢的想法。采用这种方法可以减少大约30%的大小。为了公平起见,通过避免使用Base64并改用自己编写的JSON(反)序列化器来使用URL编码友好字符(例如(['而不是{[")可以实现更大的减少。 - rinogo
显示剩余4条评论

96

@joeshmo,或者你可以直接对base64编码后的字符串进行urlencode,这样做与使用帮助函数是完全相同的,但不需要额外使用两个函数。

$str = 'Some String';

$encoded = urlencode( base64_encode( $str ) );
$decoded = base64_decode( urldecode( $encoded ) );

3
结果并不完全相同。urlencode使用3个字符来编码非有效字符,而joeshmo的解决方案只使用1个字符。这不是很大的区别,但仍然是一种浪费。 - Josef Borkovec
1
@JosefBorkovec 真的吗?那么这也意味着相同数量的字节 base64->url->encoded 可能会产生不同的结果长度,而另一种解决方案则可以给出可预测的长度,对吧? - humanityANDpeace
1
@humanityANDpeace 是的,urlencode是一个糟糕的解决方案,因为它会将某些base64字符串的大小增加三倍。而且你也不能重复使用缓冲区,因为输出比输入要大。 - Navin
10
平均而言,1到3个字符的扩展发生在64个字符中的3个上,因此它会增加9%的开销(2*3/64)。 - PaulH
如果你将 / 字符作为 URL 路径而不是 GET 参数传递,请小心处理。如果你不在两侧替换 /,它会改变你的路径。 - NeverEndingQueue
显示剩余5条评论

49

简介 我倾向于发表一些澄清,因为这里的一些答案有点误导(如果不是错误的话)。

答案是否定的,您不能只通过URL查询字符串传递base64编码的参数,因为加号会在$_GET全局数组内转换为空格。换句话说,如果您发送了test.php?myVar=stringwith+sign

//test.php
print $_GET['myVar'];

结果将是:
带符号字符串

解决这个问题的简单方法是在将base64字符串添加到查询字符串之前使用urlencode()函数对其进行编码,以转义+、=和/字符为%##代码。 例如,urlencode("带符号字符串") 返回 带符号字符串

当您处理操作时,PHP会自动处理解码查询字符串,并在填充 $_GET 全局变量时完成该过程。 例如,如果我发送test.php?myVar=带符号字符串的编码

//test.php
print $_GET['myVar'];

结果为:
stringwith+sign

您不需要对返回的$_GET字符串进行urldecode()处理,因为加号(+)会被转换为空格。
换句话说,如果我向同一目标发送test.php?myVar=stringwith%2Bsign

//test.php
$string = urldecode($_GET['myVar']);
print $string;

结果出现了意外情况:
带符号的字符串

可以使用rawurldecode()函数对输入进行安全的解码,但这是多余和不必要的。


1
不错的答案。如果问题标记为[tag:php](通常从问题的上下文中也可以明确),您可以在此网站上使用PHP代码而不需要起始和结束标签。如果您在行末添加两个空格,则会出现<br>,所以无需输入太多HTML。希望这可以帮助您,我稍微编辑了一下您的答案以进一步完善它。 - hakre
1
谢谢您提到PHP会为我们解码URL,这让我避免了掉进一个兔子洞的风险。 - Cocest
优秀的回答 -> 您不想对返回的 $_GET 字符串进行 urldecode(),因为加号会被转换为空格。但是,对输入进行 rawurldecode() 是安全的。 - MarcoZen

19

是和不是。

Base64的基本字符集在某些情况下可能与URL中使用的传统约定冲突。但是,许多Base64实现允许您更改字符集以更好地匹配URL,甚至带有一个(例如Python的urlsafe_b64encode())。

您可能面临的另一个问题是URL长度的限制,或者说——缺乏此类限制。因为标准没有规定任何最大长度,所以处理HTTP协议的浏览器、服务器、库和其他软件可能会定义自己的限制。


13

这是一个你可以尝试的base64url编码,它只是上面joeshmo代码的扩展。

function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

function base64url_decode($data) {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}

2
这适用于使用Java的Base64.getUrlEncoder().withoutPadding().encodeToString()编码的数据。 - user520458
这个 base64url_decode() 的版本破坏了我的 JSON。 - Svetoslav Marinov

5

我不认为这是安全的,因为例如在原始base64中使用了“=”字符,并且还用于区分HTTP GET请求中的参数和值。


2
如果您已安装sodium扩展并需要对二进制数据进行编码,可以使用sodium_bin2base64函数,该函数允许您选择url安全变体。
例如,编码可以像这样完成:
$string = sodium_bin2base64($binData, SODIUM_BASE64_VARIANT_URLSAFE);

和解码:

$result = sodium_base642bin($base64String, SODIUM_BASE64_VARIANT_URLSAFE);

要获取更多关于使用的信息,请查看php文档:

https://www.php.net/manual/en/function.sodium-bin2base64.php https://www.php.net/manual/en/function.sodium-base642bin.php


1

对于 URL 安全编码,例如 Python 中的 base64.urlsafe_b64encode(...),下面的代码对我来说可行了 100%

function base64UrlSafeEncode(string $input)
{
   return str_replace(['+', '/'], ['-', '_'], base64_encode($input));
}

0
理论上是可以的,只要不超过客户端或服务器的最大URL和/或查询字符串长度。
实际上,情况可能会变得有些棘手。例如,如果该值恰好包含“on”并且您保留了尾随的“==”,则可能会在ASP.NET上触发HttpRequestValidationException异常。

2
您没有提到 +、/ 或 = 字符,这些字符在某些情况下会使 URL 无效。 - Will Bickford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接