如何进行字符替换以使base64编码符合URL安全规范?

6
在研究URL安全的base64编码时,我发现这是一件非常不标准的事情。尽管PHP有大量内置函数,但没有一个用于URL安全的base64编码。在base64_encode()的手册页面上,大多数评论建议使用该函数,并包装strtr()
function base64_url_encode($input)
{
     return strtr(base64_encode($input), '+/=', '-_,');
}

我在这个领域只找到了一个Perl模块MIME::Base64::URLSafe(源代码),它内部执行以下替换操作:

sub encode ($) {
    my $data = encode_base64($_[0], '');
    $data =~ tr|+/=|\-_|d;
    return $data;
}

与上面的PHP函数不同,这个Perl版本完全删除'='(等号)字符,而不像PHP那样用','(逗号)替换它。等号是填充字符,因此Perl模块在解码时根据需要替换它们,但这种差异使得这两个实现不兼容。
最后,Python函数 urlsafe_b64encode(s) 保留'='填充符号,促使有人发布 this function 来移除填充符号,在谷歌搜索中明显显示为 'python base64 url safe'
from base64 import urlsafe_b64encode, urlsafe_b64decode

def uri_b64encode(s):
    return urlsafe_b64encode(s).strip('=')

def uri_b64decode(s):
    return urlsafe_b64decode(s + '=' * (4 - len(s) % 4))

这里的愿望是拥有一个字符串,可以在URL中直接使用而无需进一步编码,因此放弃或转换了字符'+'、'/'和'='。由于没有明确定义的标准,什么是正确的方法?
5个回答

11

有一个标准可供使用,它是RFC 3548, 第4节,Base 64 Encoding with URL and Filename Safe Alphabet(URL和文件名安全字母的Base 64编码)

从技术上讲,这种编码与之前的编码完全相同,除了表2中指示的第62和第63个字母不同。

+/需要分别替换为- (减号)_ (下划线)。任何不兼容的库都应该进行包装,以符合RFC 3548。

请注意,这需要对(pad) =字符进行URL编码,但我更喜欢这种方式,而不是对标准Base64字母表中的+/字符进行URL编码。


8

我认为并没有对错之分。但是最流行的编码方式是

'+/=' => '-_.'

这种编码方式被谷歌、雅虎(他们称之为Y64)广泛使用。Java和Ruby上我使用过的最安全的URL编码版本都支持该字符集。


提到Y64并在问题中添加一些文化,加1分。 - jmserra

2
我建议将base64_encode的输出通过urlencode进行转换。例如:
function base64_encode_url( $str )
{
    return urlencode( base64_encode( $str ) );
}

1
如果你在询问正确的方法,我会选择使用适当的URL编码而不是任意替换字符。首先对数据进行base64编码,然后使用适当的URL编码(即%<code>)进一步编码特殊字符,如“=”。

我赞成使用已有的函数,但使用urlencode()可能会增加很多额外的长度。 - Drew Stephens

0

为什么不尝试用urlencode()将其包装起来呢?文档在这里。


1
代码使用了不必要的字符。为什么不直接在一开始将二进制字符串进行urlencode呢? - recursive

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