【Ruby on Rails】加密和解密

        写在前面:之前在设计授权系统的时候,使用了 openssl 的公私钥方式,但是 Rails 中是如何对关键信息做加解密解密处理呢?今天我们先探究下Rails中的加密和解密。

        提到的内容:

  • Rails 的 key 和 credentials.yml.enc
  • 两个类,MessageVerifier 和 MessageEncryptor 
  • 签名 cookie 和加密 cookie
  • 如何读取加密的内容

一、Rails 中的 key 和 secret_key_base

        在初始化 Rails 项目的时候,框架会自动创建一个被 git ignore 的文件,config 下的 master.key。而且初始化结束后,总会提示,这个 key 千万不能丢。同时,项目还会创建一个 credentials.yml.enc 文件,它是可以放入版本库的,如果使用 `EDITOR="vim" rails credentials:edit` ,你会看到它的内容。

# aws:
#   access_key_id: 123
#   secret_access_key: 345
​
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: 19b57cbd6b06......

        保存后,这些内容会被写入到 credentials.yml.enc 文件中。网上很多文章都写到了这里就止住了。问题是,它是如何被加密的呢?

        其实,Rails内部存在自己的加密和解密的方法,它使用 master.key 对这个文件进行加密,生成凭证文件 (credentials.yml.enc),并且使用 master.key 来解密这个文件,取出里面的 secret_key_base 并保存到运行环境中,随时使用。如果你使用这几个命令可以看到它的存在。

2.6.6 :002 > Rails.application.credentials.secret_key_base
 => "19b57c..."(注意这里)

        要注意的是,这里取出的是 SecureRandom.hex(64) 生成的随机字串(我也是写的时候才发现),和后面所用的 secret 值是不同的。但是,他们名字相同。(起名很重要啊)

2.6.6 :002 > Rails.application.secrets
 => {:secret_key_base=>"53c8b81...", :secret_token=>nil}
2.6.6 :003 > Rails.application.secret_key_base
 => "53c8b818....."(注意这里,和上面的不同哦)

        这个 secret_key_base 后面还会用到,它会作为生成加密结果的 secret 使用。如果丢失了 master.key,后果将是无法解密凭证文件。

        同时,也可以把一些需要加密的数据写到这里,比如数据库连接密码等等。

2.6.6 :006 > Rails.application.credentials.mysql_password
 => "iampassword"

二、MessageVerifier 和 MessageEncryptor 

        MessageVerifier 是用来校验加密信息是否被篡改的,MessageEncryptor 用来加密数据。

        举一个简单的例子:

2.6.6 :007 > verifier = ActiveSupport::MessageVerifier.new 'iamSecret'
 => #<ActiveSupport::MessageVerifier:0x00007ff59c166068 @secret="iamSalt", @digest="SHA1", @serializer=Marshal, @options={}, @rotations=[]> 
2.6.6 :008 > signed_message = verifier.generate 'a private message'
 => "BAhJIhZhIHByaXZhdGUgbWVzc2FnZQY6BkVU--0e06f6b8d9c1ad194eecdade533e3d4f476c763e" 
2.6.6 :009 > verifier.verified(signed_message)
 => "a private message"

        这个例子里,初始化的时候用了 iamSecret 这个字符串作为 secret,这就像是弱密码一样容易被猜到,Rails 会用 secret_key_base (53c8开头的那个)来作为 secret。

        在处理敏感数据的时候,通常有2种方法,指纹和加密。指纹是指数据可读,但是不可篡改。加密就是数据不可读,更不可篡改了。

        在Rails中,常见用这两种方法处理 cookie 里的数据。通常,我们写入cookie 的内容是直接可读的,比如下图

        对于签名 cookie 和加密 cookie,不可直接读取。

三、签名 cookie

        签名 cookie 是这种结构。

InRvbSI%3D--7c000522c18ecc11013cf2512e51efc87e5fac59

        前后两部分由 -- 分隔,前面是加密后的内容,后面是签名字串。

        只需要对前面部分做一次 URL decode 和 Base64 解码即可看到原文。

Burp截图

2.6.6 :052 > Base64.strict_encode64("\"tom\"")
 => "InRvbSI="

        strict_encode64 不会增加换行符,encode64 会在60个长度字符后增加换行符(\n),并且在最后增加一个换行符(\n)。

        后面的签名可以校验前面的内容是否被篡改,Rails 使用的是 MessageVerifier,我把方法单独写出来。

# 读取 cookie 的值
cookie_value = URI.unescape("InRvbSI%3D")
# 读取 secret
secret = Rails.application.secret_key_base
# 设置 key
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(secret, "signed cookie", 1000, 64)
# 生成签名
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get("SHA1").new, key, cookie_value)
=> 7c000522c...(与浏览器比较为相同的加密结果)

四、加密 cookie

        和签名cookie不同的是,加密cookie无法在外部读取内容,它由三部分组成,且每次刷新都会变化。签名 cookie 每次刷新是不变的。

AwIPXyc%3D--rlibe2Ci5biKE9wq--OOEw1gH0cHSbo0Ly5o1iOw%3D%3D

        中间多出来的,叫 Initialization vector,在密码学里,它叫做初始化向量(英语:initialization vector,缩写为IV),或初向量,又或初始变量(starting variable,缩写为SV),是一个固定长度的输入值。一般的使用上会要求它是随机数或伪随机数(pseudorandom)。(来自百度百科)

        Rails 使用 MessageEncryptor 来进行加密,我们来进行一次解密操作。

cipher = OpenSSL::Cipher.new("aes-256-gcm")
​
secret_key_base = Rails.application.secret_key_base
secret = OpenSSL::PKCS5.pbkdf2_hmac_sha1(secret_key_base, "authenticated encrypted cookie", 1000, cipher.key_len)
​
data = Base64.strict_decode64(URI.unescape("AwIPXyc%3D"))
iv = Base64.strict_decode64(URI.unescape("rlibe2Ci5biKE9wq"))
auth_tag = Base64.strict_decode64(URI.unescape("OOEw1gH0cHSbo0Ly5o1iOw%3D%3D"))
​
cipher.decrypt
cipher.key = secret
cipher.iv  = iv
cipher.auth_tag = auth_tag
cipher.auth_data = ""
puts cipher.update(data) # => "tom" (这是解密结果)
cipher.final

        在收集资料的时候,这篇文章给了非常重要的参考,节省了大量时间,在此对作者表示感谢。

        文章参考:https://binarysolo.chapter24.blog/demystifying-cookies-in-rails-6/

五、提高Rails的安全属性

        在破解签名 cookie 和加密 cookie 的时候,所用的加密算法和salt是框架默认的,我们可以修改配置,建议在项目初始化的时候,把这几点做出修改,以提高项目安全性。

        修改算法。

config.action_dispatch.encrypted_cookie_cipher # sets the cipher to be used for encrypted cookies. This defaults to "aes-256-gcm".
config.action_dispatch.signed_cookie_digest # sets the digest to be used for signed cookies. This defaults to "SHA1".

        修改默认 salt。

config.action_dispatch.http_auth_salt # sets the HTTP Auth salt value. Defaults to 'http authentication'.
config.action_dispatch.signed_cookie_salt # sets the signed cookies salt value. Defaults to 'signed cookie'.
config.action_dispatch.encrypted_cookie_salt # sets the encrypted cookies salt value. Defaults to 'encrypted cookie'.
config.action_dispatch.encrypted_signed_cookie_salt # sets the signed encrypted cookies salt value. Defaults to 'signed encrypted cookie'.
config.action_dispatch.authenticated_encrypted_cookie_salt # sets the authenticated encrypted cookie salt. Defaults to 'authenticated encrypted cookie'.

六、自问自答

        Q:为什么要花这么多时间研究和复现 Rails 的加解密?

        A:在应用安全加固上,安全厂商提供了无侵入式的防护功能,比如动态加密,动态混淆,cookie防篡改,防暴库等功能。这些功能是在应用的外部来弥补应用自身的不足,见招拆招,打补丁。我一直在想,Rails自身的加密解密功能,是否可以提供相同的功能呢,所以先探究下 Rails 的加解密实现方案。

        写在后面:今天爆出 Apache Log4j 存 在任意代码执行漏洞,该组件存在Java JNDI注入漏洞,当程序将用户输入的数据进行日志,即可触发此漏洞,成功利用此漏洞可以在目标服务器上执行任意代码。Apache Struts2、Apache Solr、Apache Druid、Apache Flink等众多组件与大型应用均受影响,鉴于此漏洞危害巨大,利用门槛极低。写在此处,当个日记吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值