【问题标题】:Why do we use Base64?为什么我们使用 Base64?
【发布时间】:2011-04-02 01:38:35
【问题描述】:

Wikipedia

当需要对二进制数据进行编码时,通常使用 Base64 编码方案,这些二进制数据需要通过旨在处理文本数据的媒体进行存储和传输。这是为了确保数据在传输过程中保持完整而不被修改。

但是数据不是总是以二进制形式存储/传输的,因为我们机器的内存存储二进制,而这仅取决于您如何解释它吗?因此,无论您将位模式010011010110000101101110 编码为ASCII 中的Man 还是Base64 中的TWFu,您最终都会存储相同的位模式。

如果最终的编码是零和一,并且每台机器和媒体都可以处理它们,那么数据表示为 ASCII 还是 Base64 又有什么关系?

“旨在处理文本数据的媒体”是什么意思?他们可以处理二进制 => 他们可以处理任何事情。


谢谢大家,我想我现在明白了。

当我们发送数据时,我们无法确定数据是否会以与我们预期相同的格式进行解释。因此,我们发送以双方都能理解的某种格式(如 Base64)编码的数据。这样,即使发送方和接收方对相同事物的解释不同,但由于它们在编码格式上达成一致,数据也不会被错误地解释。

来自Mark Byers example

如果我想发送

Hello
world!

一种方法是像 ASCII 一样发送它

72 101 108 108 111 10 119 111 114 108 100 33

但字节 10 可能无法正确解释为另一端的换行符。所以,我们使用 ASCII 的一个子集来像这样编码它

83 71 86 115 98 71 56 115 67 110 100 118 99 109 120 107 73 61 61

以相同数量的信息传输更多数据为代价确保接收器可以按预期方式解码数据,即使接收器碰巧对字符集的其余部分有不同的解释。

【问题讨论】:

  • 历史背景:电子邮件服务器曾经是 7 位 ASCII。他们中的许多人会将高位设置为 0,因此您只需发送 7 位值。见en.wikipedia.org/wiki/Email#Content_encoding
  • 您可以(或历史上可以)仅依赖 ascii 的低 7 位在机器之间是相同的 - 或在机器之间可翻译,尤其是当并非所有机器都使用 ascii 时
  • @Martin,你在开玩笑。 Perl 很难阅读,但 base64 根本不可读。
  • @Lazer 你的图片丢失了
  • @Lazer,“但字节 10 可能无法正确解释为另一端的换行符。”为什么?双方已就 ASCII 达成一致,他们必须正确解释它!

标签: algorithm character-encoding binary ascii base64


【解决方案1】:

以下是我阅读其他人发布的内容后的理解总结:

重要!

Base64 编码并非旨在提供安全性

Base64 编码并不是为了压缩数据

我们为什么要使用 Base64

Base64 是数据的文本表示形式,仅包含 64 个字符,即字母数字字符(小写和大写)、+、/ 和 =。 这 64 个字符被认为是“安全的”,也就是说,与 \n 等许多其他字符不同,旧计算机和程序不会误解它们。

Base64 什么时候有用

我发现 base64 在将文件作为文本传输时非常有用。您获取文件的字节并将它们编码为 base64,传输 base64 字符串,然后从接收端执行相反的操作。

这与在发送电子邮件期间通过 SMTP 发送附件时使用的过程相同。

如何进行base64编码/解码

从 base64 文本到字节的转换称为解码。 从字节到 base64 文本的转换称为编码。这与其他编码/解码的命名方式有点不同。

Dotnet 和 Powershell

Microsoft 的 Dotnet 框架支持将字节编码和解码为 base64。在mscorlib 库中查找Convert 命名空间。

以下是您可以使用的 powershell 命令:

// Base64 encode PowerShell 
// See: https://adsecurity.org/?p=478
$Text='This is my nice cool text'
$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Text)
$EncodedText = [Convert]::ToBase64String($Bytes)
$EncodedText


// Convert from base64 to plain text 
[System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('VABoAGkAcwAgAGkAcwAgAG0AeQAgAG4AaQBjAGUAIABjAG8AbwBsACAAdABlAHgAdAA='))
Output>This is my nice cool text 

Bash 有一个用于 base64 编码/解码的内置命令。你可以这样使用它:

编码为base64:

echo 'hello' | base64

将 base64 编码的文本解码为普通文本:

echo 'aGVsbG8K' | base64 -d

Node.js 也支持 base64。这是一个你可以使用的类:


/**
 * Attachment class.
 * Converts base64 string to file and file to base64 string
 * Converting a Buffer to a string is known as decoding.
 * Converting a string to a Buffer is known as encoding.
 * See: https://nodejs.org/api/buffer.html
 * 
 * For binary to text, the naming convention is reversed.
 * Converting Buffer to string is encoding.
 * Converting string to Buffer is decoding.
 *  
 */
class Attachment {
    constructor(){

    }

    /**
     * 
     * @param {string} base64Str 
     * @returns {Buffer} file buffer
     */
    static base64ToBuffer(base64Str) {
        const fileBuffer = Buffer.from(base64Str, 'base64');
        // console.log(fileBuffer)
        return fileBuffer;
    }

    /**
     * 
     * @param {Buffer} fileBuffer 
     * @returns { string } base64 encoded content
     */
    static bufferToBase64(fileBuffer) {
        const base64Encoded = fileBuffer.toString('base64')
        // console.log(base64Encoded)
        return base64Encoded
    }
}

你得到的文件缓冲区是这样的:

  const fileBuffer = fs.readFileSync(path);

或者像这样:

const buf = Buffer.from('hey there');

你也可以使用一个API为你做编码和编码,这里有一个:

要进行编码,请将纯文本作为正文传递。

发布https://mk34rgwhnf.execute-api.ap-south-1.amazonaws.com/base64-encode

要解码,请将 base64 字符串作为正文传入。

发布https://mk34rgwhnf.execute-api.ap-south-1.amazonaws.com/base64-decode

【讨论】:

    【解决方案2】:

    您的第一个错误是认为 ASCII 编码和 Base64 编码是可以互换的。他们不是。它们用于不同的目的。

    • 以 ASCII 编码文本时,从文本字符串开始,然后将其转换为字节序列。
    • 在使用 Base64 编码数据时,从字节序列开始并将其转换为文本字符串。

    要了解为什么首先需要 Base64,我们需要了解一些计算历史。


    计算机以二进制(0 和 1)进行通信,但人们通常希望使用更丰富的表单数据(例如文本或图像)进行通信。为了在计算机之间传输这些数据,首先必须将其编码为 0 和 1,发送,然后再次解码。以文本为例 - 有许多不同的方式来执行这种编码。如果我们都能就单一编码达成一致,那会简单得多,但遗憾的是事实并非如此。

    最初创建了许多不同的编码(例如Baudot code),每个字符使用不同的位数,直到最终 ASCII 成为每个字符 7 位的标准。然而,大多数计算机以字节存储二进制数据,每个字节由 8 位组成,因此ASCII 不适合传输此类数据。有些系统甚至会擦除最重要的位。此外,跨系统行尾编码的差异意味着 ASCII 字符 10 和 13 有时也会被修改。

    为了解决这些问题,引入了Base64 编码。这允许您将任意字节编码为已知可以安全发送而不会损坏的字节(ASCII 字母数字字符和几个符号)。缺点是使用 Base64 编码消息会增加其长度 - 每 3 个字节的数据被编码为 4 个 ASCII 字符。

    要可靠地发送文本,您可以首先使用您选择的文本编码(例如 UTF-8)将其编码为字节,然后之后对生成的二进制数据进行 Base64 编码转换为可安全发送的文本字符串,编码为 ASCII。接收者将不得不反转这个过程来恢复原始消息。这当然要求接收方知道使用了哪些编码,而这些信息往往需要单独发送。

    从历史上看,它已被用于对电子邮件中的二进制数据进行编码,其中电子邮件服务器可能会修改行尾。一个更现代的例子是对embed image data directly in HTML source code 使用 Base64 编码。这里有必要对数据进行编码以避免像''这样的字符被解释为标签。


    这是一个工作示例:

    我希望发送一条包含两行的短信:

    你好 世界!

    如果我将其作为 ASCII(或 UTF-8)发送,它将如下所示:

    72 101 108 108 111 10 119 111 114 108 100 33
    

    字节 10 在某些系统中已损坏,因此我们可以将这些字节进行 base 64 编码为 Base64 字符串:

    SGVsbG8Kd29ybGQh

    使用 ASCII 编码时如下所示:

    83 71 86 115 98 71 56 75 100 50 57 121 98 71 81 104
    

    这里的所有字节都是已知的安全字节,因此任何系统都不会损坏此消息的可能性很小。我可以发送这个而不是我的原始消息,让接收者反转该过程以恢复原始消息。

    【讨论】:

    • “大多数现代通信协议不会损坏数据” - 尽管例如电子邮件可能会在保存邮件时将字符串“\nFrom”替换为“\n>From”邮件到邮箱。或者 HTTP 标头是换行符终止的,没有可逆的方式来转义数据中的换行符(行继续合并空白),因此您也不能将任意 ASCII 转储到它们中。 base64 优于 just 7 位安全,它是 alpha-numeric-and-=+/ 安全的。
    • “缺点是使用 Base64 编码消息会增加其长度 - 每 3 个字节的数据编码为 4 个字节。”它如何增加到 4 个字节?它不会仍然是 3*8 = 24 位吗?
    • @Lazer:不。看看你自己的例子 - “Man”是base-64编码为“TWFu”。 3 个字节 -> 4 个字节。这是因为允许输入是 2^8 = 256 个可能的字节中的任何一个,而输出只使用其中的 2^6 = 64 个(和 =,以帮助指示数据的长度)。每四重输出 8 位被“浪费”,以防止输出包含任何“令人兴奋”的字符,即使输入包含。
    • 重新声明“当你在 Base64 中编码数据时,你从一个字节序列开始并将其转换为文本字符串”可能会有所帮助“当你在 Base64 中编码数据时,你从一个字节序列并将其转换为仅由 ASCII 值组成的字节序列”。仅由 ASCII 字符组成的字节序列是 SMTP 所要求的,这就是为什么使用 Base64(和带引号的可打印)作为内容传输编码的原因。很棒的概述!
    • 我发现一篇反向引用的帖子谈论这个“如果我们不这样做,那么存在某些字符可能被错误解释的风险。例如,换行符,如 0x0A 和 0x0D,控制^C、^D 和 ^Z 等字符在某些平台上被解释为文件结尾,NULL 字节作为文本字符串的结尾,字节高于 0x7F(非 ASCII),我们在 HTML/ 中使用 Base64 编码XML 文档,以避免将 '' 等字符解释为标签。”
    【解决方案3】:

    Base64 而不是转义特殊字符

    我会给你一个非常不同但真实的例子:我编写 javascript 代码以在浏览器中运行。 HTML 标记具有 ID 值,但对 ID 中的有效字符有限制。

    但我希望我的 ID 无损地引用我的文件系统中的文件。现实中的文件中可以包含各种奇怪而奇妙的字符,从感叹号、重音字符、波浪号,甚至表情符号!我不能这样做:

    <div id="/path/to/my_strangely_named_file!@().jpg">
        <img src="http://myserver.com/path/to/my_strangely_named_file!@().jpg">
        Here's a pic I took in Moscow.
    </div>
    

    假设我想运行一些这样的代码:

    # ERROR
    document.getElementById("/path/to/my_strangely_named_file!@().jpg");
    

    我认为这段代码在执行时会失败。

    使用 Base64,我可以参考一些复杂的东西,而不必担心哪种语言允许哪些特殊字符以及哪些需要转义:

    document.getElementById("18GerPD8fY4iTbNpC9hHNXNHyrDMampPLA");
    

    与使用 MD5 或其他哈希函数不同,您可以反转编码以找出真正有用的数据。

    我希望我在几年前就知道 Base64。我会避免用“encodeURIComponent”和str.replace(‘\n’,’\\n’) 撕掉我的头发

    SSH 传输文本:

    如果您尝试通过 ssh 传递复杂的数据(例如,一个 dotfile,以便您可以获得您的 shell 个性化),祝您在没有 Base 64 的情况下顺利完成。这就是您使用 base 64 的方式(我知道您可以使用 SCP,但这需要多个命令 - 这会使 ssh 到服务器的键绑定复杂化):

    【讨论】:

      【解决方案4】:

      在 XML 中编码二进制数据

      假设您想在 XML 文档中嵌入几个图像。图像是二进制数据,而 XML 文档是文本。但是 XML 不能处理嵌入的二进制数据。那你是怎么做到的呢?

      一种选择是使用 base64 对图像进行编码,将二进制数据转换为 XML 可以处理的文本。

      代替:

      <images>
        <image name="Sally">{binary gibberish that breaks XML parsers}</image>
        <image name="Bobby">{binary gibberish that breaks XML parsers}</image>
      </images>
      

      你这样做:

      <images>
        <image name="Sally" encoding="base64">j23894uaiAJSD3234kljasjkSD...</image>
        <image name="Bobby" encoding="base64">Ja3k23JKasil3452AsdfjlksKsasKD...</image>
      </images>
      

      XML 解析器将能够正确解析 XML 文档并提取图像数据。

      【讨论】:

      • 这可能是微软旧的.mht 格式的工作原理(html 文件 + 单个文件中的图像)。
      【解决方案5】:

      为什么/我们如何使用 Base64 编码?

      Base64 是一种具有 75% 效率的二进制到文本编码方案。它用于使典型的二进制数据(例如图像)可以通过传统的“非 8 位干净”通道安全地发送。 在早期的电子邮件网络中(直到 1990 年代初),大多数电子邮件消息都是 7 位 US-ASCII 字符集中的纯文本。如此多的早期通信协议标准被设计为在“非 8 位干净”的“7 位”通信链路上工作。 方案效率是输入中的位数与编码输出中的位数之间的比率。 十六进制(Base16)也是一种效率高达50%的二进制转文本编码方案。

      Base64 编码步骤(简化):

      1. 二进制数据以连续的 24 位(3 字节)块排列。
      2. 每个 24 位块分为四个部分,每个部分 6 位。
      3. 每个 6 位组都转换为它们对应的 Base64 字符值,即 Base64 编码将三个八位字节转换为四个编码字符。输出字节与输入字节的比率为 4:3(33% 开销)。
      4. 有趣的是,相同字符的编码方式会有所不同,具体取决于它们在被编码为四个字符的三八位组中的位置。
      5. 接收者必须反转此过程才能恢复原始消息。

      【讨论】:

        【解决方案6】:

        什么是“媒体是 旨在处理文本数据”?

        这些协议旨在处理文本(通常只有英文文本)而不是二进制数据(如 .png 和 .jpg 图像)。

        他们可以处理二进制 => 他们可以 处理任何事情。

        但反之则不成立。旨在表示文本的协议可能会不正确地处理碰巧包含以下内容的二进制数据:

        • 字节 0x0A 和 0x0D,用于行尾,因平台而异。
        • 其他控制字符,如 0x00(NULL = C 字符串终止符)、0x03(文本结束)、0x04(传输结束)或 0x1A(DOS 文件结束),它们可能会过早地发出数据结束的信号。
        • 0x7F 以上的字节(如果协议是为 ASCII 设计的)。
        • UTF-8 无效的字节序列。

        因此,您不能只通过基于文本的协议发送二进制数据。您仅限于表示非空格非控制 ASCII 字符的字节,其中有 94 个。选择 Base 64 的原因是使用 2 的幂更快,而 64 是最大的.

        一个问题。那个怎么样 系统仍然没有就共同点达成一致 如此常见的编码技术 UTF-8?

        至少在 Web 上,他们大部分都有。 A majority of sites use UTF-8.

        西方的问题是,有很多旧软件将 1 字节 = 1 个字符作为 UTF-8 编码,无法使用 UTF-8。

        东方的问题是他们对 GB2312 和 Shift_JIS 等编码的依恋。

        而且微软似乎还没有克服选择了错误的 UTF 编码这一事实。如果要使用 Windows API 或 Microsoft C 运行时库,则仅限于 UTF-16 或区域设置的“ANSI”编码。这使得使用 UTF-8 很痛苦,因为您必须一直进行转换。

        【讨论】:

          【解决方案7】:

          除了其他(有些冗长的)答案:即使忽略仅支持 7 位 ASCII 的旧系统,以文本模式提供二进制数据的基本问题是:

          • 换行符通常以文本模式转换。
          • 必须注意不要将 NUL 字节视为文本字符串的结尾,这在任何具有 C 血统的程序中都很容易做到。

          【讨论】:

          • 还有一些控制字符,如 ^C、^D 和 ^Z,在某些平台上会被解释为文件结尾。
          【解决方案8】:

          为什么不关注the RFC that currently defines Base64

          数据的基本编码用于 存储或转移的许多情况
          环境中的数据,也许是 遗留原因,仅限于 US-ASCII [1] data.Base 编码可以 也可用于新应用 没有遗留限制, 仅仅因为它使之成为可能 用文本操作对象 编辑。

          过去,不同的应用程序 有不同的要求和 因此有时实施基地 编码略有不同 方法。今天,协议规范 有时在 一般,特别是“base64”, 没有准确的描述或 参考。多用途互联网邮件 扩展(MIME)[4] 经常被使用 作为base64的参考,没有 考虑后果 换行或非字母 人物。这样做的目的 规范是建立共同的 字母和编码 考虑因素。这将有望 减少其他方面的歧义 文件,带来更好的 互操作性。

          Base64 最初的设计目的是作为多用途 Internet 邮件扩展的一部分,允许将二进制数据附加到电子邮件中。

          【讨论】:

            【解决方案9】:

            更多的是媒体验证字符串编码,所以我们要确保处理应用程序可以接受数据(例如,不包含表示 EOL 的二进制序列)

            假设您想在电子邮件中使用编码 UTF-8 发送二进制数据 -- 如果 1 和 0 流创建的 序列 不是有效的 Unicode,则电子邮件可能无法正确显示UTF-8 编码。

            当我们想要对 URL 本身中的 URL 无效的字符进行编码时,URL 中也会发生相同类型的事情:

            http://www.foo.com/hello我的朋友 -> http://www.foo.com/hello%20my%20friend

            这是因为我们想通过一个认为该空间有异味的系统发送一个空间。

            我们所做的只是确保在已知良好、可接受和无害的位序列与另一个字面位序列之间存在一对一的映射,并且处理应用程序不区分 编码。

            在您的示例中,man 可能是第一种形式的有效 ASCII;但通常您可能希望传输随机二进制值(即在电子邮件中发送图像):

            MIME 版本:1.0
            内容描述:“a.gif 的 Base64 编码”
            内容类型:图片/gif;名称="a.gif"
            内容传输编码:Base64
            内容处置:附件;文件名="a.gif"

            在这里,我们看到 GIF 图像以 base64 编码为电子邮件的一部分。电子邮件客户端读取标题并对其进行解码。由于编码,我们可以确定 GIF 不包含任何可能被解释为协议的内容,并且我们避免插入 SMTP 或 POP 可能认为重要的数据。

            【讨论】:

            • 太棒了——这个解释让它点击了。这不是为了混淆或压缩数据,而只是为了避免使用可以解释为协议的特殊序列。
            【解决方案10】:

            我发现它很方便的一个例子是尝试embed binary data in XML。一些二进制数据被 SAX 解析器误解了,因为这些数据实际上可以是任何东西,包括 XML 特殊字符。 Base64 在发送端对数据进行编码,在接收端进行解码,解决了这个问题。

            【讨论】:

            • +1 -- 但这绝不是特定于 SAX 的。任何 XML 解析器都会发生这种情况,即 DOM 或 XLINQ。
            • @Billy:是的,绝对是。我只是碰巧为该应用程序使用了 SAX 解析器。
            • 不同的引擎,例如 SAX 解析器可能会以不同的方式(不同的控制字符)解释一些 ASCII 值。因此,这里的想法是使用具有普遍意义的 ASCII 子集。对吗?
            • @Lazer:对。当您尝试将其解释为 ASCII 时,未编码的二进制数据中会偶然包含控制字符(在这种情况下不是)。
            【解决方案11】:

            大多数计算机以 8 位二进制格式存储数据,但这不是必需的。一些机器和传输媒体一次只能处理 7 位(甚至更少)。这样的媒体将以 7 位的倍数解释流,因此如果您要发送 8 位数据,您将不会在另一端收到您期望的内容。 Base-64 只是解决此问题的一种方法:您将输入编码为 6 位格式,通过您的介质发送,然后在接收端将其解码回 8 位格式。

            【讨论】:

            • 为什么流在 7 位后中断会出现问题。最后,对方机器将所有数据通过流接收,那么它可以选择8位格式显示吗?我脑子怎么了!
            【解决方案12】:

            为文本数据设计的媒体当然最终也是二进制的,但文本媒体通常使用某些二进制值作为控制字符。此外,文本媒体可能会拒绝将某些二进制值视为非文本。

            Base64 编码将二进制数据编码为只能在文本媒体中解释为文本的值,并且没有任何特殊字符和/或控制字符,因此数据也将在文本媒体中保存。

            【讨论】:

            • 所以就像 Base64 一样,大多数情况下,源和目标都会以相同的方式解释数据,因为它们很可能会以相同的方式解释这 64 个字符,即使它们以不同的方式解释控制字符方法。对吗?
            • 他们的数据甚至可能在传输过程中被破坏。例如,如果服务器和客户端的操作系统不匹配并且传输被标记为文本模式,许多 FTP 程序会将行尾从 13,10 重写为 10 或反之亦然。 FTP 只是我想到的第一个例子,它不是一个好例子,因为 FTP 确实支持二进制模式。
            • @nhnb:我认为 FTP 是一个很好的例子,因为它表明文本模式不适合需要二进制数据的东西。
            • 什么是文本媒体?
            【解决方案13】:

            “旨在处理文本数据的媒体”是什么意思?

            在 ASCII 统治世界的时代,处理非 ASCII 值令人头疼。人们不惜一切代价在不丢失信息的情况下通过网络传输这些信息。

            【讨论】:

            • 实际上,在过去,ASCII 甚至没有在任何地方使用。许多协议都有单独的文本模式和二进制模式来传输数据,不幸的是当时电子邮件没有。文本模式是必要的,正是因为没有单一的文本编码统治世界,而不是 ASCII;每个计算机网络都有自己喜欢的编码,因此有网关的工作是将交换的文本转换为本地编码,这样日本公司就可以在没有 mojibake 的情况下向美国商业顾问发送电子邮件。显然,这种转换在发送二进制数据时是不可取的。
            猜你喜欢
            • 2012-02-21
            • 2020-11-11
            • 1970-01-01
            • 1970-01-01
            • 2016-05-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多