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

Posted

技术标签:

【中文标题】在 URL 中传递 base64 编码的字符串【英文标题】:Passing base64 encoded strings in URL 【发布时间】:2010-11-25 09:00:48 【问题描述】:

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

【问题讨论】:

不,不是 - 链接的问题较新。所以它使链接的问题成为这个问题的副本...... @serge 哪一个? 【参考方案1】:

还有其他 base64 规范。 (详见表格here)。但基本上你需要 65 个字符来编码:26 个小写字母 + 26 个大写字母 + 10 个数字 = 62。

您还需要两个 ['+', '/'] 和一个填充字符 '='。但它们都不是 url 友好的,所以只需为它们使用不同的字符,你就可以了。上图中的标准字符是 ['-', '_'],但您可以使用其他字符,只要您将它们解码相同即可,无需与他人共享。

我建议只编写自己的助手。喜欢这些来自php manual page for base64_encode的cmets:

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


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

【讨论】:

很好的解决方案,除了 URL 中没有保留逗号。我建议使用“~”(波浪号)或“。” (点)代替。 @kralyk:我建议按照rodrigo-silveira 的回答建议使用urlencode。创建两个新函数来节省 url 长度中的几个字符,这就像通过窗户而不是仅仅通过门进入你的房子。 @MarcoDemaio,不知道它会如何使用,不可能说它只是几个字符。每个编码字符都有三倍的长度,为什么“+++...”不是一个有效的 base64 字符串? URL 有浏览器限制,将 URL 增加三倍可能会使您达到这些限制。 @RandalSchwartz 波浪号 URL 安全的。来自 RFC3986:unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 由于, 应该被编码为%2C,我建议使用._- 而不是-_,,就像en.wikipedia.org/wiki/Base64#Variants_summary_table 中唯一保留尾随= 的变体一样 【参考方案2】:

不,您需要对其进行 url 编码,因为 base64 字符串可以包含“+”、“=”和“/”字符,这些字符可能会改变数据的含义 - 看起来像一个子文件夹。

有效的 base64 字符如下。

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=

【讨论】:

URLencoding 浪费空间,尤其是 base64 本身会留下许多未使用的字符。 我不确定我是否理解您在说什么 - URL 编码不会改变除上面列表中的最后三个字符之外的任何字符,这是为了防止它们被错误解释,因为它们有URLS 中的其他含义。 base64 也是如此,原始数据可以是二进制或任何形式,但它被编码为可以使用简单协议轻松传输的形式。 首先,你也应该转义'+',因为它可能会被转换成空格。其次,至少有几个字符可以安全地在 URL 中使用,而不是在“标准”字符集中使用。在某些情况下,您的方法甚至可以将传输数据的大小增加三倍;而用其他字符替换这些字符将在保持相同长度的同时达到目的。这也是相当标准的解决方案。 en.wikipedia.org/wiki/Base64#URL_applications — 它清楚地表明转义“使字符串不必要地变长”,并提到了备用字符集变体。 @MichałGórny 如果您使用 JSON 作为 GET 参数,Base 64 编码将(取决于您的数据)可能会减小请求字符串的大小。 (在你说这是一个愚蠢的想法之前,我们在查询字符串中使用 JSON 来促进深度链接到我们的应用程序。)对于我们的应用程序,这种方法实现了大约 30% 的减少。 (公平地说,通过完全避免使用 Base64 而是编写我们自己的使用 URL 编码友好字符的 JSON(反)序列化程序(例如 ([' 而不是 ["),可以实现更大的减少。【参考方案3】:

@joeshmo 或者,您可以直接对 base64 编码的字符串进行 urlencode,而不是编写辅助函数。这将与您的辅助函数完全相同,但不需要两个额外的函数。

$str = 'Some String';

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

【讨论】:

结果不完全一样。 urlencode用3个字符编码无效字符,joeshmo的方案用1个。差别不大,但还是很浪费。 @JosefBorkovec 真的吗?那么这也意味着相同的字节数 base64->url->encoded 可能是各种不同的结果长度,而另一种解决方案给出了可预测的长度,对吧? @humanityANDpeace 是的,urlencode 是一个糟糕的解决方案,因为它将某些 base64 字符串的大小增加了三倍。您也不能重用缓冲区,因为输出大于输入。 从 1 到 3 个字符的扩展平均发生在 64 个字符中的 3 个上,因此开销为 9% (2*3/64) 如果您不是将 / 字符作为 GET 参数传递,而是作为 URL 中的路径传递,请注意它。如果你不在两边都用其他东西替换/,它会改变你的路径。【参考方案4】:

介绍性说明我倾向于发表一些澄清,因为这里的一些答案有点误导(如果不是不正确的话)。

答案是否定的,您不能简单地在 URL 查询字符串中传递 base64 编码参数,因为加号会在 $_GET 全局数组中转换为 SPACE。换句话说,如果您将 test.php?myVar=stringwith+sign 发送到

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

结果将是:stringwith sign

解决此问题的简单方法是简单地 urlencode() 您的 base64 字符串,然后将其添加到查询字符串以将 +、= 和 / 字符转义为 %## 代码。 例如,urlencode("stringwith+sign") 返回stringwith%2Bsign

当您处理该操作时,PHP 会在填充 $_GET 全局变量时自动解码查询字符串。 例如,如果我将 test.php?myVar=stringwith%2Bsign 发送到

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

结果是:stringwith+sign

您确实想要urldecode()返回的$_GET字符串,因为+将被转换为空格。 换句话说,如果我将相同的 test.php?myVar=stringwith%2Bsign 发送到

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

结果出乎意料:stringwith sign

rawurldecode() 输入是安全的,但是它是多余的,因此是不必要的。

【讨论】:

不错的答案。如果问题被标记为php(通常也可以从问题的上下文中清楚地看出),您可以在此站点上使用不带开始和结束标记的 PHP 代码。如果在行尾添加两个空格,您将看到<br>,因此无需输入太多 html。我希望这会有所帮助,我稍微编辑了您的答案以进一步改进它。 感谢您提到 PHP 会为您解码 URL。这样我就不会掉进兔子洞了。 好答案 -> 您不想对返回的 $_GET 字符串进行 urldecode(),因为 + 将被转换为空格。但是, rawurldecode() 输入是安全的,【参考方案5】:

是和不是。

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

您可能面临的另一个问题是 URL 长度的限制,或者更确切地说 - 没有这样的限制。由于标准没有规定任何最大长度,浏览器、服务器、库和其他使用 HTTP 协议的软件可能会定义自己的限制。

【讨论】:

【参考方案6】:

它是一个你可以尝试的 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));

【讨论】:

这适用于用Java的Base64.getUrlEncoder().withoutPadding().encodeToString()编码的数据 这个版本的 base64url_decode() 破坏了我的 JSON。【参考方案7】:

我不认为这是安全的,因为例如“=”字符用于原始 base 64,也用于将参数与 HTTP GET 中的值区分开来。

【讨论】:

【参考方案8】:

理论上可以,只要不超过客户端或服务器的最大 url 和/或查询字符串长度即可。

在实践中,事情可能会变得有点棘手。例如,如果值恰好包含“on”并且您在结尾的“==”中留下,它可以在 ASP.NET 上触发 HttpRequestValidationException。

【讨论】:

您没有提到 +、/ 或 = 字符在某些情况下会使 url 无效。【参考方案9】:

对于 url 安全编码,例如 Python 中的 base64.urlsafe_b64encode(...),下面的代码对我来说 100% 有效

function base64UrlSafeEncode(string $input)

   return str_replace(['+', '/'], ['-', '_'], base64_encode($input));

【讨论】:

【参考方案10】:

如果您安装了钠扩展并且需要对二进制数据进行编码,您可以使用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

【讨论】:

以上是关于在 URL 中传递 base64 编码的字符串的主要内容,如果未能解决你的问题,请参考以下文章

Base64编解码是什么?

BASE64编码的字符进行URL传输丢失特殊字符的问题

在 c# 中等效于在 c 编程中生成的要在 URL 查询字符串中发送的“Base64”编码的加密字节字符串

base64编码是做啥用的?

URL安全的Base64编码,解码

浏览器端将语音转换为URL格式的字符串(base64 位编码)