Shiraaz Moollatjie
Posted on October 6, 2020
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.
Posted on October 6, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
April 3, 2022