Base64 모듈

Base64 모듈

Base64 모듈은 다음을 위한 메서드를 제공해요.

  • 비-ASCII 문자를 포함한 바이너리 문자열을 출력 가능한 ASCII 문자열로 인코딩.
  • 그렇게 인코딩된 문자열을 디코딩.

Base64는 바이너리 데이터가 허용되지 않거나 지원되지 않는 상황에서 흔히 쓰여요.

  • HTML·CSS 파일 또는 URL 안의 이미지.
  • 이메일 첨부 파일.

Base64 인코딩 문자열은 원본보다 약 1/3 더 길어요. 이 모듈은 인코딩·디코딩 메서드 쌍을 세 개 제공해요.

  • 인코딩·디코딩에 어떤 문자 집합을 쓸지.
  • "패딩(padding)"을 쓸지 여부.
  • 인코딩 문자열에 개행 문자가 들어갈지 여부.

출처: Ruby 3.3 API

본문

이 페이지의 예시는 포함 프로그램이 다음을 실행했다고 가정해요.

require 'base64'

인코딩 문자 집합 (Encoding Character Sets)

Base64 인코딩 문자열은 64개 문자 집합으로만 구성돼요.

  • ('A'..'Z').

  • ('a'..'z').

  • ('0'..'9').

  • =, '패딩' 문자.

  • %w[+ /]: RFC-2045 준수, URL에는 안전하지 않음.

  • %w[- _]: RFC-4648 준수, URL에 안전.

URL에 넣거나 URL에서 가져올 Base64 문자열을 다룬다면 RFC-4648 준수 쌍인 Base64.urlsafe_encode64Base64.urlsafe_decode64를 선택해야 해요. 그 외에는 이 모듈의 어떤 쌍이라도 골라도 돼요. RFC-2045 준수 쌍은 Base64.encode64/Base64.decode64, Base64.strict_encode64/Base64.strict_decode64예요.

패딩 (Padding)

Base64 인코딩은 입력 바이트 3개(triplet)를 출력 문자 4개(quartet)로 바꿔요.

인코드 메서드의 패딩 — 패딩(인코딩 문자열을 뒤에 = 0~2개로 늘리는 것)은 Base64.encode64, Base64.strict_encode64, 그리고 기본적으로 Base64.urlsafe_encode64가 수행해요.

Base64.encode64('s')                         # => "cw==\n"
Base64.strict_encode64('s')                  # => "cw=="
Base64.urlsafe_encode64('s')                 # => "cw=="
Base64.urlsafe_encode64('s', padding: false) # => "cw"

패딩을 적용하면 인코딩 문자열은 항상 길이가 4n이 돼요(n은 0 이상 정수).

# n = 1:  3 bytes => 4 characters.
Base64.strict_encode64('123')      # => "MDEy"
# n = 2:  6 bytes => 8 characters.
Base64.strict_encode64('123456')   # => "MDEyMzQ1"
  • 길이 3n의 입력 바이트는 길이 4n의 패딩 없는 출력 문자를 생성해요.
    # n = 1:  4 bytes => 8 characters.
    Base64.strict_encode64('1234')     # => "MDEyMw=="
    # n = 2:  7 bytes => 12 characters.
    Base64.strict_encode64('1234567')  # => "MDEyMzQ1Ng=="
    
  • 길이 3n+1의 입력 바이트는 끝에 패딩 문자 2개를 가진 길이 4(n+1)의 패딩 출력 문자를 생성해요.
    # n = 1:  5 bytes => 8 characters.
    Base64.strict_encode64('12345')    # => "MDEyMzQ="
    # n = 2:  8 bytes => 12 characters.
    Base64.strict_encode64('12345678') # => "MDEyMzQ1Njc="
    
  • 길이 3n+2의 입력 바이트는 끝에 패딩 문자 1개를 가진 길이 4(n+1)의 패딩 출력 문자를 생성해요.

패딩을 억제하면, 양의 정수 n에 대해:

# n = 1:  3 bytes => 4 characters.
Base64.urlsafe_encode64('123', padding: false)      # => "MDEy"
# n = 2:  6 bytes => 8 characters.
Base64.urlsafe_encode64('123456', padding: false)   # => "MDEyMzQ1"
  • 길이 3n의 입력 바이트는 길이 4n의 패딩 없는 출력 문자를 생성해요.
    # n = 1:  4 bytes => 6 characters.
    Base64.urlsafe_encode64('1234', padding: false)     # => "MDEyMw"
    # n = 2:  7 bytes => 10 characters.
    Base64.urlsafe_encode64('1234567', padding: false)  # => "MDEyMzQ1Ng"
    
  • 길이 3n+1의 입력 바이트는 끝에 패딩 문자 2개를 가진 길이 4n+2의 패딩 없는 출력 문자를 생성해요.
    # n = 1:  5 bytes => 7 characters.
    Base64.urlsafe_encode64('12345', padding: false)    # => "MDEyMzQ"
    # m = 2:  8 bytes => 11 characters.
    Base64.urlsafe_encode64('12345678', padding: false) # => "MDEyMzQ1Njc"
    
  • 길이 3n+2의 입력 바이트는 끝에 패딩 문자 1개를 가진 길이 4n+3의 패딩 없는 출력 문자를 생성해요.

디코드 메서드의 패딩 — Base64 디코드 메서드는 모두 패딩을 지원하지만(필수는 아님) 그래요. Base64.decode64는 패딩 크기를 검사하지 않아요.

Base64.decode64("MDEyMzQ1Njc") # => "01234567"
Base64.decode64("MDEyMzQ1Njc=") # => "01234567"
Base64.decode64("MDEyMzQ1Njc==") # => "01234567"

Base64.strict_decode64는 패딩 크기를 엄격히 강제해요.

Base64.strict_decode64("MDEyMzQ1Njc")   # Raises ArgumentError
Base64.strict_decode64("MDEyMzQ1Njc=")  # => "01234567"
Base64.strict_decode64("MDEyMzQ1Njc==") # Raises ArgumentError

Base64.urlsafe_decode64str의 패딩을 허용하는데, 있으면 반드시 맞아야 해요.

Base64.urlsafe_decode64("MDEyMzQ1Njc") # => "01234567"
Base64.urlsafe_decode64("MDEyMzQ1Njc=") # => "01234567"
Base64.urlsafe_decode64("MDEyMzQ1Njc==") # Raises ArgumentError.

개행 (Newlines)

Base64.encode64 또는 Base64.urlsafe_encode64가 반환한 인코딩 문자열은 60문자 시퀀스마다 개행 문자가 내장돼 있고, 비어 있지 않다면 끝에도 하나 있어요.

# No newline if empty.
encoded = Base64.encode64("\x00" *  0)
encoded.index("\n") # => nil

# Newline at end of short output.
encoded = Base64.encode64("\x00" *  1)
encoded.size        # => 4
encoded.index("\n") # => 4

# Newline at end of longer output.
encoded = Base64.encode64("\x00" * 45)
encoded.size        # => 60
encoded.index("\n") # => 60

# Newlines embedded and at end of still longer output.
encoded = Base64.encode64("\x00" * 46)
encoded.size                          # => 65
encoded.rindex("\n")                  # => 65
encoded.split("\n").map {|s| s.size } # => [60, 4]

인코딩할 문자열 자체에 개행이 있어도 Base64로 인코딩돼요.

  #   Base64.encode64("\n\n\n") # => "CgoK\n"
s = "This is line 1\nThis is line 2\n"
Base64.encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"

Public Instance Methods

decode64(str)

RFC-2045 준수 Base64 인코딩 문자열 str을 디코딩한 문자열을 반환해요.

s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
Base64.decode64(s) # => "This is line 1\nThis is line 2\n"

str의 비-Base64 문자는 무시돼요. 개행 문자, -, /가 여기 해당돼요.

Base64.decode64("\x00\n-_") # => ""

str의 패딩은 (틀려도) 무시돼요.

Base64.decode64("MDEyMzQ1Njc")   # => "01234567"
Base64.decode64("MDEyMzQ1Njc=")  # => "01234567"
Base64.decode64("MDEyMzQ1Njc==") # => "01234567"
# File lib/base64.rb, line 241
def decode64(str)
  str.unpack1("m")
end

encode64(bin)

bin을 RFC-2045 준수 Base64 인코딩한 문자열을 반환해요.

RFC 2045에 따라 반환 문자열은 URL에 안전하지 않은 + 또는 / 문자를 포함할 수 있어요.

Base64.encode64("\xFB\xEF\xBE") # => "++++\n"
Base64.encode64("\xFF\xFF\xFF") # => "////\n"

반환 문자열은 패딩을 포함할 수 있어요.

Base64.encode64('*') # => "Kg==\n"

반환 문자열은 개행 문자로 끝나고, 충분히 길면 내장 개행 문자가 하나 이상 있을 수도 있어요.

Base64.encode64('*') # => "Kg==\n"
Base64.encode64('*' * 46)
# => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq\nKg==\n"

인코딩할 문자열 자체에 개행이 있어도 일반 Base64로 인코딩돼요.

Base64.encode64("\n\n\n") # => "CgoK\n"
s = "This is line 1\nThis is line 2\n"
Base64.encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK\n"
# File lib/base64.rb, line 219
def encode64(bin)
  [bin].pack("m")
end

strict_decode64(str)

RFC-2045 준수 Base64 인코딩 문자열 str을 디코딩한 문자열을 반환해요.

s = "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK"
Base64.strict_decode64(s) # => "This is line 1\nThis is line 2\n"

str의 비-Base64 문자는 허용되지 않아요. 개행 문자, -, /가 여기 해당돼요.

Base64.strict_decode64("\n") # Raises ArgumentError
Base64.strict_decode64('-')  # Raises ArgumentError
Base64.strict_decode64('_')  # Raises ArgumentError

str의 패딩은 있으면 반드시 맞아야 해요.

Base64.strict_decode64("MDEyMzQ1Njc")   # Raises ArgumentError
Base64.strict_decode64("MDEyMzQ1Njc=")  # => "01234567"
Base64.strict_decode64("MDEyMzQ1Njc==") # Raises ArgumentError
# File lib/base64.rb, line 297
def strict_decode64(str)
  str.unpack1("m0")
end

strict_encode64(bin)

bin을 RFC-2045 준수 Base64 인코딩한 문자열을 반환해요.

RFC 2045에 따라 반환 문자열은 URL에 안전하지 않은 + 또는 / 문자를 포함할 수 있어요.

Base64.strict_encode64("\xFB\xEF\xBE") # => "++++"
Base64.strict_encode64("\xFF\xFF\xFF") # => "////"

반환 문자열은 패딩을 포함할 수 있어요.

Base64.strict_encode64('*') # => "Kg=="

반환 문자열은 길이와 무관하게 개행 문자를 갖지 않아요.

Base64.strict_encode64('*') # => "Kg=="
Base64.strict_encode64('*' * 46)
# => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg=="

인코딩할 문자열 자체에 개행이 있어도 일반 Base64로 인코딩돼요.

Base64.strict_encode64("\n\n\n") # => "CgoK"
s = "This is line 1\nThis is line 2\n"
Base64.strict_encode64(s) # => "VGhpcyBpcyBsaW5lIDEKVGhpcyBpcyBsaW5lIDIK"
# File lib/base64.rb, line 273
def strict_encode64(bin)
  [bin].pack("m0")
end

urlsafe_decode64(str)

RFC-4648 준수 Base64 인코딩 문자열 str을 디코딩한 값을 반환해요.

str은 비-Base64 문자를 포함할 수 없어요.

Base64.urlsafe_decode64('+')  # Raises ArgumentError.
Base64.urlsafe_decode64('/')  # Raises ArgumentError.
Base64.urlsafe_decode64("\n") # Raises ArgumentError.

str의 패딩은 있으면 반드시 맞아야 해요.

Base64.urlsafe_decode64("MDEyMzQ1Njc") # => "01234567"
Base64.urlsafe_decode64("MDEyMzQ1Njc=") # => "01234567"
Base64.urlsafe_decode64("MDEyMzQ1Njc==") # Raises ArgumentError.
# File lib/base64.rb, line 351
def urlsafe_decode64(str)
  # NOTE: RFC 4648 does say nothing about unpadded input, but says that
  # "the excess pad characters MAY also be ignored", so it is inferred that
  # unpadded input is also acceptable.
  if !str.end_with?("=") && str.length % 4 != 0
    str = str.ljust((str.length + 3) & ~3, "=")
    str.tr!("-_", "+/")
  else
    str = str.tr("-_", "+/")
  end
  strict_decode64(str)
end

urlsafe_encode64(bin, padding: true)

bin을 RFC-4648 준수 Base64 인코딩한 값을 반환해요.

RFC 4648에 따라 반환 문자열은 URL에 안전하지 않은 + 또는 /를 포함하지 않고, 그 대신 URL에 안전한 -_를 포함할 수 있어요.

Base64.urlsafe_encode64("\xFB\xEF\xBE") # => "----"
Base64.urlsafe_encode64("\xFF\xFF\xFF") # => "____"

기본적으로 반환 문자열은 패딩을 가질 수 있어요.

Base64.urlsafe_encode64('*') # => "Kg=="

선택적으로 패딩을 억제할 수 있어요.

Base64.urlsafe_encode64('*', padding: false) # => "Kg"

반환 문자열은 길이와 무관하게 개행 문자를 갖지 않아요.

Base64.urlsafe_encode64('*') # => "Kg=="
Base64.urlsafe_encode64('*' * 46)
# => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg=="
# File lib/base64.rb, line 328
def urlsafe_encode64(bin, padding: true)
  str = strict_encode64(bin)
  str.chomp!("==") or str.chomp!("=") unless padding
  str.tr!("+/", "-_")
  str
end