loading...
Cover image for A journey with good old base64

A journey with good old base64

shiraazm profile image Shiraaz Moollatjie ・4 min read

I did some work around base64 in go. I found it pretty interesting and thought that it would make a nice article to write about

What is base64?

Base64 is a binary to text encoding scheme that represents binary data in plain ASCII format. It's designed to give your application a way of transferring binary data across text protocols. The technical specification for base64 can be found at RFC 4648

Common use cases

The most common use cases for base64 are:

  • Embedding images in html pages. The idea is to reduce the number of server roundtrips made for your website as well as reduce the number of dependencies on your web page
  • Returning binary data (such as images) in JSON or XML apis
  • When using HTTP basic authentication, the username/password combination is encoded into base64 format
  • Saving binary files into your database if a BLOB/CLOB data type is not supported

Leave a comment if you used base64 for anything!

Let's encode in go

You can follow the examples on your local pc using the associated go playground

What can we use?

Go has base64 support available under the encoding/base64 package. It is largely driven by an Encoding type with four default encodings available:

  • StdEncoding
  • RawStdEncoding
  • URLEncoding
  • RawURLEncoding

For this post, I will not go into the URLEncoding and RawURLEncoding types.

Coding examples - Simple encoding & decoding

We'll start our examples with some base text that we want to encode:

Programs must be written for people to read, and only incidentally for machines to execute.

You can copy & paste this into an online base64 encoder/decoder for external verification. To encode some text, we can simply do the following:

package main

import (
    "encoding/base64"
    "fmt"
    "log"
)

func main() {
    str := "Programs must be written for people to read, and only incidentally for machines to execute."

    encStd := base64.StdEncoding.EncodeToString([]byte(str))
    encRawStd := base64.RawStdEncoding.EncodeToString([]byte(str))

    decStd, err := base64.StdEncoding.DecodeString(encStd)
    if err != nil {
        log.Fatalf("code better for blog posts: %v", err)
    }

    decRawStd, err := base64.RawStdEncoding.DecodeString(encRawStd)
    if err != nil {
        log.Fatalf("code better for blog posts: %v", err)
    }

    fmt.Printf("original string:     %s\n", str)
    fmt.Printf("encoded  string:     %s\n", encStd)
    fmt.Printf("raw encoded  string: %s\n\n", encRawStd)
    fmt.Printf("decoded  string:     %s\n", decRawStd)
    fmt.Printf("raw decoded  string: %s\n", decStd)
}

This gives you the output:

original string:     Programs must be written for people to read, and only incidentally for machines to execute.
encoded  string:     UHJvZ3JhbXMgbXVzdCBiZSB3cml0dGVuIGZvciBwZW9wbGUgdG8gcmVhZCwgYW5kIG9ubHkgaW5jaWRlbnRhbGx5IGZvciBtYWNoaW5lcyB0byBleGVjdXRlLg==
raw encoded  string: UHJvZ3JhbXMgbXVzdCBiZSB3cml0dGVuIGZvciBwZW9wbGUgdG8gcmVhZCwgYW5kIG9ubHkgaW5jaWRlbnRhbGx5IGZvciBtYWNoaW5lcyB0byBleGVjdXRlLg

decoded  string:     Programs must be written for people to read, and only incidentally for machines to execute.
raw decoded  string: Programs must be written for people to read, and only incidentally for machines to execute.

Comments

There is a subtle difference between RawStdEncoding and StdEncoding and that would be the presence of padding characters. The StdEcoding type includes padding characters in its output. The default padding character for base64 is = and it is customizable.

The two encoded formats are also not interchangeable. So adding these snippets will cause errors during decoding (and encoding):

decStd, err = base64.StdEncoding.DecodeString(encRawStd)
    if err != nil {
        fmt.Printf("cannot use a raw encoded base64 string for standard decoding: %v\n", err)
    }

decRawStd, err = base64.RawStdEncoding.DecodeString(encStd)
    if err != nil {
        fmt.Printf("cannot use a standard encoded base64 string for raw decoding: %v\n", err)
    }

Let's do some slightly more complex encoding

In this next example, let us try to base64 encode an image. This program will write the go logo to stdout in base64 format.

package main

import (
    "encoding/base64"
    "io/ioutil"
    "log"
    "net/http"
    "os"
)

func main() {
  // Encode a base64 image. Using a URL because it's simpler than using a file.
    resp, err := http.Get("https://golang.org/lib/godoc/images/go-logo-blue.svg")
    if err != nil {
        log.Fatalf("code better for blog posts: %v", err)
    }

    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalf("code better for blog posts: %v", err)
    }

    encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout)
    defer encoder.Close()

    encoder.Write(b)
}

This yields the following output (viewable on an online base64 decoder)

PHN2ZyBoZWlnaHQ9Ijc4IiB2aWV3Qm94PSIwIDAgMjA3IDc4IiB3aWR0aD0iMjA3IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxnIGZpbGw9IiMwMGFjZDciIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHBhdGggZD0ibTE2LjIgMjQuMWMtLjQgMC0uNS0uMi0uMy0uNWwyLjEtMi43Yy4yLS4zLjctLjUgMS4xLS41aDM1LjdjLjQgMCAuNS4zLjMuNmwtMS43IDIuNmMtLjIuMy0uNy42LTEgLjZ6Ii8+PHBhdGggZD0ibTEuMSAzMy4zYy0uNCAwLS41LS4yLS4zLS41bDIuMS0yLjdjLjItLjMuNy0uNSAxLjEtLjVoNDUuNmMuNCAwIC42LjMuNS42bC0uOCAyLjRjLS4xLjQtLjUuNi0uOS42eiIvPjxwYXRoIGQ9Im0yNS4zIDQyLjVjLS40IDAtLjUtLjMtLjMtLjZsMS40LTIuNWMuMi0uMy42LS42IDEtLjZoMjBjLjQgMCAuNi4zLjYuN2wtLjIgMi40YzAgLjQtLjQuNy0uNy43eiIvPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU1KSI+PHBhdGggZD0ibTc0LjEgMjIuM2MtNi4zIDEuNi0xMC42IDIuOC0xNi44IDQuNC0xLjUuNC0xLjYuNS0yLjktMS0xLjUtMS43LTIuNi0yLjgtNC43LTMuOC02LjMtMy4xLTEyLjQtMi4yLTE4LjEgMS41LTYuOCA0LjQtMTAuMyAxMC45LTEwLjIgMTkgLjEgOCA1LjYgMTQuNiAxMy41IDE1LjcgNi44LjkgMTIuNS0xLjUgMTctNi42LjktMS4xIDEuNy0yLjMgMi43LTMuNy0zLjYgMC04LjEgMC0xOS4zIDAtMi4xIDAtMi42LTEuMy0xLjktMyAxLjMtMy4xIDMuNy04LjMgNS4xLTEwLjkuMy0uNiAxLTEuNiAyLjUtMS42aDM2LjRjLS4yIDIuNy0uMiA1LjQtLjYgOC4xLTEuMSA3LjItMy44IDEzLjgtOC4yIDE5LjYtNy4yIDkuNS0xNi42IDE1LjQtMjguNSAxNy05LjggMS4zLTE4LjktLjYtMjYuOS02LjYtNy40LTUuNi0xMS42LTEzLTEyLjctMjIuMi0xLjMtMTAuOSAxLjktMjAuNyA4LjUtMjkuMyA3LjEtOS4zIDE2LjUtMTUuMiAyOC0xNy4zIDkuNC0xLjcgMTguNC0uNiAyNi41IDQuOSA1LjMgMy41IDkuMSA4LjMgMTEuNiAxNC4xLjYuOS4yIDEuNC0xIDEuN3oiLz48cGF0aCBkPSJtMTA3LjIgNzcuNmMtOS4xLS4yLTE3LjQtMi44LTI0LjQtOC44LTUuOS01LjEtOS42LTExLjYtMTAuOC0xOS4zLTEuOC0xMS4zIDEuMy0yMS4zIDguMS0zMC4yIDcuMy05LjYgMTYuMS0xNC42IDI4LTE2LjcgMTAuMi0xLjggMTkuOC0uOCAyOC41IDUuMSA3LjkgNS40IDEyLjggMTIuNyAxNC4xIDIyLjMgMS43IDEzLjUtMi4yIDI0LjUtMTEuNSAzMy45LTYuNiA2LjctMTQuNyAxMC45LTI0IDEyLjgtMi43LjUtNS40LjYtOCAuOXptMjMuOC00MC40Yy0uMS0xLjMtLjEtMi4zLS4zLTMuMy0xLjgtOS45LTEwLjktMTUuNS0yMC40LTEzLjMtOS4zIDIuMS0xNS4zIDgtMTcuNSAxNy40LTEuOCA3LjggMiAxNS43IDkuMiAxOC45IDUuNSAyLjQgMTEgMi4xIDE2LjMtLjYgNy45LTQuMSAxMi4yLTEwLjUgMTIuNy0xOS4xeiIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9nPjwvZz48L3N2Zz4=

When base64 is not recommended

Base64 is of course not a silver bullet. There are cases where base64 is not recommended.

Do not use base64 for storing passwords

It is highly recommended to NOT use abse64 for storing passwords. It seems easy enough to encode data into a format that's hard to read for humans. However, base64 is birectional, so whatever is encoded can be easily decoded. This highlights an important distinction that base64 is an encoding scheme, not an encryption scheme.

Do not replace all your website images with base64 variants

While it is possible to convert your images to base64, it comes with the following tradeoffs:

  • The base64 version of your image is larger (heuristically ~30% larger)
  • You lose the ability to cache images on the client side

Summary

In this post, we wrote some simple go programs to show off its capabilities. These snippets could be used as the base of your own applications.

Base64 is indeed a neat piece of computer science research. However, it is not a silver bullet and there are pros and cons when using the encoding in your application. Always use the best tool for the job.

Discussion

pic
Editor guide