Skip to content

Instantly share code, notes, and snippets.

@zedalaye
Created May 19, 2022 14:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zedalaye/4857d0eb097b59b79abb3eb7e8a3335d to your computer and use it in GitHub Desktop.
Save zedalaye/4857d0eb097b59b79abb3eb7e8a3335d to your computer and use it in GitHub Desktop.
Load sensitive code from encrypted files in Ruby

Sensitive

This code demonstrate how to load sensitive code from encrypted files at runtime.

$ ruby sample.rb

Now, protect the sensitive code

$ ruby protect_sensitive.rb sensitive_methods.rb
  • A master.key file will be generated
  • sensitive_methods.rb is encrypted into sensitive_methods.rb.enc and renamed into sensitive_methods.rb.hidden

Run sample again :

$ ruby sample.rb

Remove, rename or corrupt the master.key :

$ mv master.key master.key.skipped

Sensitive code is not loaded.

Now you can publish your code ignoring master.key and *.hidden files, no one would run your sensitive code unless they have access to the master.key

Warning

The application code must be designed to work even if the sensitive code is not loaded

#!/bin/env ruby
file = ARGV[0]
abort "Syntax: #{$0} <file>" if file.nil?
abort "#{file} does not exist" unless File.exists?(file)
require 'openssl'
require 'base64'
require 'json'
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.encrypt
key = { key: cipher.random_key, iv: cipher.random_iv }
buf = ''
File.open(file + '.enc', 'wb') do |outf|
File.open(file, 'rb') do |inf|
while inf.read(4096, buf)
outf << cipher.update(buf)
end
outf << cipher.final
end
end
key.transform_values! { |v| Base64.encode64(v) }
File.open('master.key', 'wb') do |f|
f << key.to_json
end
File.rename(file, file + '.hidden')
class MyClass
def self.hello
puts "Hello World"
end
end
#!/bin/env ruby
require_relative 'sensitive'
require_relative 'public_class'
require_sensitive 'sensitive_methods', 'master.key'
puts MyClass.hello
puts MyClass.sensitive if MyClass.respond_to?(:sensitive)
def require_sensitive(file, keyfile, outside_binding = binding)
if File.exists?(file + '.rb')
require_relative(file)
elsif File.exists?(keyfile) && File.exists?(file + '.rb.enc')
require 'openssl'
require 'base64'
require 'json'
key = JSON.load_file(keyfile)
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.decrypt
cipher.key = Base64.decode64(key['key'])
cipher.iv = Base64.decode64(key['iv'])
begin
code = ''
buf = ''
File.open(file + '.rb.enc') do |f|
while f.read(4096, buf)
code << cipher.update(buf)
end
code << cipher.final
end
eval(code, outside_binding, file)
rescue OpenSSL::Cipher::CipherError
# Ignore bad decrypt
end
end
end
class MyClass
def self.sensitive
puts "I'm a sensitive method"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment