information security programming in ruby

25
Information Security Programming in Ruby @nahi

Upload: hiroshi-nakamura

Post on 09-Feb-2017

467 views

Category:

Software


3 download

TRANSCRIPT

Page 1: Information security programming in ruby

Information Security Programming in Ruby

@nahi

Page 2: Information security programming in ruby

@nahi - Twitter, GithubSoftware Engineer at https://www.treasuredata.com

OSS developer and enthusiast;committer of CRuby and JRuby

Information Security Specialist

Page 3: Information security programming in ruby

Information Security Programming in Ruby

scripts:https://github.com/nahi/ruby-crypt/tree/master/odrk05

Page 4: Information security programming in ruby

ReferencesJUS 2003 “PKI入門 - Ruby/OpenSSLを触りながら学ぶPKI”https://github.com/nahi/ruby-crypt/raw/master/jus-pki.ppt

RubyKaigi 2006 “セキュアアプリケーションプログラミング”https://github.com/nahi/ruby-crypt/blob/master/rubykaigi2006/RubyKaigi2006_SAP_20060610.pdf

RubyConf 2012 “Ruby HTTP clients comparison”http://www.slideshare.net/HiroshiNakamura/rubyhttp-clients-comparison

Page 5: Information security programming in ruby

Information Security Programming

Confidentially

Authentication

Integrity

(Availability)

(Privacy)

Page 6: Information security programming in ruby

(D) S for external C

[F] Encryption in S

[G] Encryption in C

[E] authentication

(C) S for internal C

(B) C for external S

7 Implementation Patterns(A) C for internal S

(A)

(A)

(B)

(B)

(C)

(D)

[F]

[G]

[E][E]

Orange: Implementation targetGray: External system

Page 7: Information security programming in ruby

(D) S for external C

[F] Encryption in S

[G] Encryption in C

[E] authentication

(C) S for internal C

(B) C for external S

7 Implementation Patterns(A) C for internal S

(A)

(A)

(B)

(B)

(C)

(D)

[F]

[G]

[E][E]

Orange: Implementation targetGray: External system

Page 8: Information security programming in ruby

… in Ruby(A) C for internal S

(B) C for external S

(C) S for internal C

(D) S for external C

[E] authentication

[F] Encryption in S

[G] Encryption in C

(A)

(A)

(B)

(B)

(C)

[E]

[F]

[G]

(D)

[E]

Blue: AcceptableOrange: PitfallsRed: No way

Page 9: Information security programming in ruby

Protected communication

Fixed server authentication

➔ SSL configuration:CBC, SSLv3.0, compression,TLSv1.0, RC4, DHE1024, …

➔ Fails for wrong endpoint

(A) C for internal S

(A)

(A)

Page 10: Information security programming in ruby

SSL configurationrequire 'httpclient'client = HTTPClient.newclient.get('https://www.ruby-lang.org/en/').status

% ruby a1.rbok: "/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA"ok: "/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2"ok: "/OU=Domain Control Validated/CN=*.ruby-lang.org"Protocol version: TLSv1.2Cipher: ["ECDHE-RSA-AES128-GCM-SHA256", "TLSv1/SSLv3", 128, 128]State: SSLOK : SSL negotiation finished successfully

Page 11: Information security programming in ruby

Fails for wrong endpointrequire 'httpclient'client = HTTPClient.newclient.get('https://hyogo-9327.herokussl.com/en/').status

% ruby -d a2.rbok: "/C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA"ok: "/C=BE/O=GlobalSign nv-sa/CN=GlobalSign Domain Validation CA - SHA256 - G2"ok: "/OU=Domain Control Validated/CN=*.ruby-lang.org"Protocol version: TLSv1.2Cipher: ["ECDHE-RSA-AES128-GCM-SHA256", "TLSv1/SSLv3", 128, 128]State: SSLOK : SSL negotiation finished successfullyException `OpenSSL::SSL::SSLError' - hostname "hyogo-9327.herokussl.com" does not match the server certificate

Page 12: Information security programming in ruby

require 'aws-sdk'

class KMSEncryptor CTX = { 'purpose' => 'odrk05 demonstration' } GCM_IV_SIZE = 12; GCM_TAG_SIZE = 16

def initialize(region, key_id) @region, @key_id = region, key_id @kms = Aws::KMS::Client.new(region: @region) end

def generate_data_key resp = @kms.generate_data_key_without_plaintext( key_id: @key_id, encryption_context: CTX, key_spec: 'AES_128' ) resp.ciphertext_blob end

def with_key(wrapped_key) key = nil begin key = @kms.decrypt( ciphertext_blob: wrapped_key, encryption_context: CTX ).plaintext yield key ensure # TODO: confirm that key is deleted from memory key.tr!("\0-\xff".force_encoding('BINARY'), "\0") end end

Page 13: Information security programming in ruby

Fails for weak connectionrequire 'httpclient'client = HTTPClient.newclient.ssl_config.ssl_version = :TLSv1_2client.get('https://localhost:17443/').status

=begin% ruby a3.rbSSL_connect returned=1 errno=0 state=SSLv3 read server hello A: wrong version number (OpenSSL::SSL::SSLError)=end

Page 14: Information security programming in ruby

Net::HTTP samplerequire 'net/https'class NetHTTPClient < Net::HTTP require 'httpclient' def do_start if $DEBUG && @use_ssl self.verify_callback = HTTPClient::SSLConfig.new(nil). method(:default_verify_callback) end super end def on_connect if $DEBUG && @use_ssl ssl_socket = @socket.io if ssl_socket.respond_to?(:ssl_version) warn("Protocol version: #{ssl_socket.ssl_version}") end warn("Cipher: #{ssl_socket.cipher.inspect}") warn("State: #{ssl_socket.state}") end super endend# =>

# =>client = NetHTTPClient.new( "www.ruby-lang.org", 443)client.use_ssl = trueclient.cert_store = store = OpenSSL::X509::Store.newstore.set_default_pathsclient.get("/")

Page 15: Information security programming in ruby

Protected communication

Restricted server authentication

➔ SSL configuration

➔ Fails for revoked server

(B) C for external S

(A)

(A)

(B)

(B)

Page 16: Information security programming in ruby

Revocation checkrequire 'httpclient' # >= 2.7.0client = HTTPClient.newclient.get('https://test-sspev.verisign.com:2443/test-SSPEV-revoked-verisign.html').status

% ruby b.rb # => 200% jruby b.rb # => 200% jruby -J-Dcom.sun.security.enableCRLDP=true \ -J-Dcom.sun.net.ssl.checkRevocation=true b.rbOpenSSL::SSL::SSLError: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: Certificate has been revoked, reason: UNSPECIFIED, revocation date: Thu Oct 30 06:29:37 JST 2014, authority: CN=Symantec Class 3 EV SSL CA - G3, OU=Symantec Trust Network, O=Symantec Corporation, C=US

OpenSSL...?

Page 17: Information security programming in ruby

Protected communication

Restricted client authentication

➔ SSL configuration➔ Server key management➔ Certificate rotation➔ Fails for unexpected clients

(C) S for internal C

(C)

Page 18: Information security programming in ruby

WEBrick SSL serverrequire 'webrick/https'require 'logger'logger = Logger.new(STDERR)server = WEBrick::HTTPServer.new( BindAddress: "localhost", Logger: logger, Port: 17443, DocumentRoot: '/dev/null', SSLEnable: true, SSLCACertificateFile: 'ca-chain.cert', SSLCertificate: OpenSSL::X509::Certificate.new( File.read('server.cert')), SSLPrivateKey: OpenSSL::PKey::RSA.new( File.read('server.key')),)basic_auth=WEBrick::HTTPAuth::BasicAuth.new( Logger: logger, Realm: 'auth', UserDB: WEBrick::HTTPAuth::Htpasswd.new( 'htpasswd'))# =>

# =>server.mount('/hello', WEBrick::HTTPServlet::ProcHandler.new( ->(req, res) { basic_auth.authenticate(req, res) res['content-type'] = 'text/plain' res.body = 'hello' }))trap(:INT) do server.shutdownend

t = Thread.new { Thread.current.abort_on_exception = true server.start}while server.status != :Running sleep 0.1 raise unless t.alive?endputs $$t.join

Page 19: Information security programming in ruby

Protected communication

Client authentication

➔ SSL configuration➔ Server key management➔ Certificate rotation➔ Fails for unexpected clients➔ Recovery from key compromise

You have better solutions (Apache, Nginx, ELB, …)

(D) S for external C

(C)

(D)

Page 20: Information security programming in ruby

Client authentication

On unprotected network

➔ Cipher algorithm➔ Tamper detection➔ Constant time operation

Use well-known library

[E] authentication

[E][E]

Page 21: Information security programming in ruby

Data protection at rest

➔ Cipher algorithm➔ Encryption key management

◆ Storage◆ Usage authn / authz◆ Usage auditing◆ Rotation

➔ Tamper detection➔ Processing throughput / latency

[F] Encryption in S / [G] in C

[F]

[G]

Page 22: Information security programming in ruby

require 'aws-sdk'

class KMSEncryptor CTX = { 'purpose' => 'odrk05 demonstration' } GCM_IV_SIZE = 12; GCM_TAG_SIZE = 16

def initialize(region, key_id) @region, @key_id = region, key_id @kms = Aws::KMS::Client.new(region: @region) end

def generate_data_key resp = @kms.generate_data_key_without_plaintext( key_id: @key_id, encryption_context: CTX, key_spec: 'AES_128' ) resp.ciphertext_blob end

def with_key(wrapped_key) key = nil begin key = @kms.decrypt( ciphertext_blob: wrapped_key, encryption_context: CTX ).plaintext yield key ensure # TODO: confirm that key is deleted from memory key.tr!("\0-\xff".force_encoding('BINARY'), "\0") end end

Page 23: Information security programming in ruby

def encrypt(wrapped_key, plaintext) with_key(wrapped_key) do |key| cipher = OpenSSL::Cipher::Cipher.new('aes-128-gcm') iv = OpenSSL::Random.random_bytes(GCM_IV_SIZE) cipher.encrypt; cipher.key = key;cipher.iv = iv iv + cipher.update(plaintext) + cipher.final end end

def decrypt(wrapped_key, ciphertext) with_key(wrapped_key) do |key| iv, data = ciphertext.unpack("a#{GCM_IV_SIZE}a*") auth_tag = data.slice!(data.bytesize - GCM_TAG_SIZE, GCM_TAG_SIZE) cipher = OpenSSL::Cipher::Cipher.new('aes-128-gcm') cipher.decrypt; cipher.key = key; cipher.iv = iv cipher.auth_tag = auth_tag cipher.update(data) + cipher.final end endend

encryptor = KMSEncryptor.new('ap-northeast-1', 'alias/nahi-test-tokyo')# generate key for each data, customer, or somethingwrapped_key = encryptor.generate_data_key

plaintext = File.read(__FILE__)ciphertext = encryptor.encrypt(wrapped_key, plaintext)# save wrapped_key and ciphertext in DB, File or somewhere# restore wrapped_key and ciphertext from DB, File or somewhereputs encryptor.decrypt(wrapped_key, ciphertext)

jruby-openssl does not support aes-gcm…-> next page

Page 24: Information security programming in ruby

if defined?(JRuby) require 'java' java_import 'javax.crypto.Cipher' java_import 'javax.crypto.SecretKey' java_import 'javax.crypto.spec.SecretKeySpec' java_import 'javax.crypto.spec.GCMParameterSpec'

class KMSEncryptor # Overrides def encrypt(wrapped_key, plaintext) with_key(wrapped_key) do |key| cipher = Cipher.getInstance('AES/GCM/PKCS5Padding') iv = OpenSSL::Random.random_bytes(GCM_IV_SIZE) spec = GCMParameterSpec.new(GCM_TAG_SIZE * 8, iv.to_java_bytes) cipher.init(1, SecretKeySpec.new(key.to_java_bytes, 0, key.bytesize, 'AES'), spec) ciphertext = String.from_java_bytes( cipher.doFinal(plaintext.to_java_bytes), Encoding::BINARY) iv + ciphertext end end

# Overrides def decrypt(wrapped_key, ciphertext) with_key(wrapped_key) do |key| cipher = Cipher.getInstance('AES/GCM/PKCS5Padding') iv, data = ciphertext.unpack("a#{GCM_IV_SIZE}a*") spec = GCMParameterSpec.new(GCM_TAG_SIZE * 8, iv.to_java_bytes) cipher.init(2, SecretKeySpec.new(key.to_java_bytes, 0, key.bytesize, 'AES'), spec) String.from_java_bytes(cipher.doFinal(data.to_java_bytes), Encoding::BINARY) end end endend

aes-128-gcm in JRuby!

Page 25: Information security programming in ruby

… in Ruby(A) C for internal S

(B) C for external S

(C) S for internal C

(D) S for external C

[E] authentication

[F] Encryption in S

[G] Encryption in C

(A)

(A)

(B)

(B)

(C)

[E]

[F]

[G]

(D)

[E]

Blue: AcceptableOrange: PitfallsRed: No way