prudkohliad

How to securely delete (zeroize) a Ruby String


Sensitive data such as passwords, API tokens or private keys often live as simple String objects in memory. In Ruby however, simply dropping a reference doesn’t guarantee the underlying data is erased – memory may linger until garbage‑collected, or the object remains in memory until the process ends. In this post I’ll show how to securely delete a Ruby string by zeroizing (overwriting its contents with zero‑bytes) before dropping it, so you leave fewer traces of sensitive material in memory.

Fiddle

Fiddle is Ruby’s standard library for dynamically loading and interfacing with C libraries. It allows you to call C functions, access C data structures, and work with native libraries directly from Ruby. Since Ruby String objects are essentially byte arrays, Fiddle can work with their raw memory, making it possible to manipulate or pass string data to C functions efficiently.

We’ll use Fiddle here to peek at the actual memory of a string so you can see zeroization in action.

# frozen_string_literal: true

require "fiddle"

secret = "secret_data".dup # [1]
original_bytesize = secret.bytesize
pointer = Fiddle::Pointer[secret]
hex = pointer[0, original_bytesize].unpack1('H*')

p hex
# => "7365637265745f64617461"

[1] Since # frozen_string_literal: true is enabled, we duplicate the string to ensure it is mutable. Frozen literals cannot be zeroized because Ruby will raise a FrozenError. This won’t be the case for a real application, since you will either generate byte secrets directly in your application or read some sensitive data from database or network.

Zeroization of a String

Let’s write a method for String that will fill the underlying memory with zeroes.

class String
  def zeroize!
    bytesize.times do |i| 
	    setbyte(i, 0)
    end
    
    clear
  end
end

The method iterates over each byte of the string and sets it to 0, then clears the string content with clear.

Verifying with Fiddle

Extending the initial example, let’s do the following:

# ...

p pointer[0, original_bytesize].unpack1('H*') 
# before: "7365637265745f64617461"

secret.zeroize!

p pointer[0, original_bytesize].unpack1('H*') 
# after:  "0000000000000000000000"

p secret
# ""

We can see that the value of the secret has changed to an empty string – that’s because we have also used the clear method – and the underlying memory is all zeroes.

Limitations

While zeroizing strings in Ruby is possible, the language has limitations when it comes to other types. Values like Integer, Float, or Time are immutable and stored in a way that makes safe in-memory erasure impossible at the Ruby level. True secure zeroization for these types would require handling memory directly in C. For applications with high-security requirements, Ruby may not be the best choice for managing sensitive data in memory.


Comments

🚧 Please enable JavaScript to use the comments feature

More posts