Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
张重言
rails
提交
3b506ee0
R
rails
项目概览
张重言
/
rails
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
rails
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
3b506ee0
编写于
7月 06, 2017
作者:
A
Assain
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add expires_at, expires_in, and purpose meta_data to messages.
上级
d1281cdc
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
192 addition
and
6 deletion
+192
-6
activesupport/lib/active_support/message_encryptor.rb
activesupport/lib/active_support/message_encryptor.rb
+6
-5
activesupport/lib/active_support/messages/metadata.rb
activesupport/lib/active_support/messages/metadata.rb
+55
-0
activesupport/test/message_encryptor_test.rb
activesupport/test/message_encryptor_test.rb
+43
-1
activesupport/test/metadata/shared_metadata_tests.rb
activesupport/test/metadata/shared_metadata_tests.rb
+88
-0
未找到文件。
activesupport/lib/active_support/message_encryptor.rb
浏览文件 @
3b506ee0
...
@@ -4,6 +4,7 @@
...
@@ -4,6 +4,7 @@
require
"base64"
require
"base64"
require_relative
"core_ext/array/extract_options"
require_relative
"core_ext/array/extract_options"
require_relative
"message_verifier"
require_relative
"message_verifier"
require_relative
"messages/metadata"
module
ActiveSupport
module
ActiveSupport
# MessageEncryptor is a simple way to encrypt values which get stored
# MessageEncryptor is a simple way to encrypt values which get stored
...
@@ -87,14 +88,15 @@ def initialize(secret, *signature_key_or_options)
...
@@ -87,14 +88,15 @@ def initialize(secret, *signature_key_or_options)
# Encrypt and sign a message. We need to sign the message in order to avoid
# Encrypt and sign a message. We need to sign the message in order to avoid
# padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
# padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
def
encrypt_and_sign
(
value
)
def
encrypt_and_sign
(
value
,
expires_at:
nil
,
expires_in:
nil
,
purpose:
nil
)
verifier
.
generate
(
_encrypt
(
value
))
data
=
Messages
::
Metadata
.
wrap
(
value
,
expires_at:
expires_at
,
expires_in:
expires_in
,
purpose:
purpose
)
verifier
.
generate
(
_encrypt
(
data
))
end
end
# Decrypt and verify a message. We need to verify the message in order to
# Decrypt and verify a message. We need to verify the message in order to
# avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
# avoid padding attacks. Reference: http://www.limited-entropy.com/padding-oracle-attacks.
def
decrypt_and_verify
(
value
)
def
decrypt_and_verify
(
data
,
purpose:
nil
)
_decrypt
(
verifier
.
verify
(
value
)
)
Messages
::
Metadata
.
verify
(
_decrypt
(
verifier
.
verify
(
data
)),
purpose
)
end
end
# Given a cipher, returns the key length of the cipher to help generate the key of desired size
# Given a cipher, returns the key length of the cipher to help generate the key of desired size
...
@@ -103,7 +105,6 @@ def self.key_len(cipher = default_cipher)
...
@@ -103,7 +105,6 @@ def self.key_len(cipher = default_cipher)
end
end
private
private
def
_encrypt
(
value
)
def
_encrypt
(
value
)
cipher
=
new_cipher
cipher
=
new_cipher
cipher
.
encrypt
cipher
.
encrypt
...
...
activesupport/lib/active_support/messages/metadata.rb
0 → 100644
浏览文件 @
3b506ee0
# frozen_string_literal: true
require
"time"
module
ActiveSupport
module
Messages
#:nodoc:
class
Metadata
#:nodoc:
def
initialize
(
expires_at
,
purpose
)
@expires_at
,
@purpose
=
expires_at
,
purpose
end
class
<<
self
def
wrap
(
message
,
expires_at:
nil
,
expires_in:
nil
,
purpose:
nil
)
if
expires_at
||
expires_in
||
purpose
{
"value"
=>
message
,
"_rails"
=>
{
"exp"
=>
pick_expiry
(
expires_at
,
expires_in
),
"pur"
=>
purpose
.
to_s
}
}
else
message
end
end
def
verify
(
message
,
purpose
)
metadata
=
extract_metadata
(
message
)
if
metadata
.
nil?
message
if
purpose
.
nil?
elsif
metadata
.
match?
(
purpose
.
to_s
)
&&
metadata
.
fresh?
message
[
"value"
]
end
end
private
def
pick_expiry
(
expires_at
,
expires_in
)
if
expires_at
expires_at
.
utc
.
iso8601
(
3
)
elsif
expires_in
expires_in
.
from_now
.
utc
.
iso8601
(
3
)
end
end
def
extract_metadata
(
message
)
if
message
.
is_a?
(
Hash
)
&&
message
.
key?
(
"_rails"
)
new
(
message
[
"_rails"
][
"exp"
],
message
[
"_rails"
][
"pur"
])
end
end
end
def
match?
(
purpose
)
@purpose
==
purpose
end
def
fresh?
@expires_at
.
nil?
||
Time
.
now
.
utc
<
Time
.
iso8601
(
@expires_at
)
end
end
end
end
activesupport/test/message_encryptor_test.rb
浏览文件 @
3b506ee0
...
@@ -4,6 +4,7 @@
...
@@ -4,6 +4,7 @@
require
"openssl"
require
"openssl"
require
"active_support/time"
require
"active_support/time"
require
"active_support/json"
require
"active_support/json"
require_relative
"metadata/shared_metadata_tests"
class
MessageEncryptorTest
<
ActiveSupport
::
TestCase
class
MessageEncryptorTest
<
ActiveSupport
::
TestCase
class
JSONSerializer
class
JSONSerializer
...
@@ -106,8 +107,15 @@ def test_messing_with_aead_values_causes_failures
...
@@ -106,8 +107,15 @@ def test_messing_with_aead_values_causes_failures
assert_aead_not_decrypted
(
encryptor
,
[
text
,
iv
,
auth_tag
[
0
..-
2
]]
*
"--"
)
assert_aead_not_decrypted
(
encryptor
,
[
text
,
iv
,
auth_tag
[
0
..-
2
]]
*
"--"
)
end
end
private
def
test_backwards_compatibility_decrypt_previously_encrypted_messages_without_metadata
secret
=
"
\xB7\xF0\xBC
W
\xB1\x18
`
\xAB\xF0\x81\x10\xA4
$
\xF4
4
\xEC\xA1\xDC\xC1\xDD
D
\xAF\xA9\xB8\x14\xCD\x18\x9A\x99
\x80
)"
encryptor
=
ActiveSupport
::
MessageEncryptor
.
new
(
secret
,
cipher:
"aes-256-gcm"
)
encrypted_message
=
"9cVnFs2O3lL9SPvIJuxBOLS51nDiBMw=--YNI5HAfHEmZ7VDpl--ddFJ6tXA0iH+XGcCgMINYQ=="
assert_equal
"Ruby on Rails"
,
encryptor
.
decrypt_and_verify
(
encrypted_message
)
end
private
def
assert_aead_not_decrypted
(
encryptor
,
value
)
def
assert_aead_not_decrypted
(
encryptor
,
value
)
assert_raise
(
ActiveSupport
::
MessageEncryptor
::
InvalidMessage
)
do
assert_raise
(
ActiveSupport
::
MessageEncryptor
::
InvalidMessage
)
do
encryptor
.
decrypt_and_verify
(
value
)
encryptor
.
decrypt_and_verify
(
value
)
...
@@ -132,3 +140,37 @@ def munge(base64_string)
...
@@ -132,3 +140,37 @@ def munge(base64_string)
::
Base64
.
strict_encode64
(
bits
)
::
Base64
.
strict_encode64
(
bits
)
end
end
end
end
class
MessageEncryptorMetadataTest
<
ActiveSupport
::
TestCase
include
SharedMessageMetadataTests
setup
do
@secret
=
SecureRandom
.
random_bytes
(
32
)
@encryptor
=
ActiveSupport
::
MessageEncryptor
.
new
(
@secret
,
encryptor_options
)
end
private
def
generate
(
message
,
**
options
)
@encryptor
.
encrypt_and_sign
(
message
,
options
)
end
def
parse
(
data
,
**
options
)
@encryptor
.
decrypt_and_verify
(
data
,
options
)
end
def
encryptor_options
;
end
end
class
MessageEncryptorMetadataMarshalTest
<
MessageEncryptorMetadataTest
private
def
encryptor_options
{
serializer:
Marshal
}
end
end
class
MessageEncryptorMetadataJSONTest
<
MessageEncryptorMetadataTest
private
def
encryptor_options
{
serializer:
MessageEncryptorTest
::
JSONSerializer
.
new
}
end
end
activesupport/test/metadata/shared_metadata_tests.rb
0 → 100644
浏览文件 @
3b506ee0
# frozen_string_literal: true
module
SharedMessageMetadataTests
def
setup
@message
=
{
"credit_card_no"
=>
"5012-6784-9087-5678"
,
"card_holder"
=>
{
"name"
=>
"Donald"
}
}
super
end
def
teardown
travel_back
super
end
def
test_encryption_and_decryption_with_same_purpose
assert_equal
@message
,
parse
(
generate
(
@message
,
purpose:
"checkout"
),
purpose:
"checkout"
)
assert_equal
@message
,
parse
(
generate
(
@message
))
string_message
=
"address: #23, main street"
assert_equal
string_message
,
parse
(
generate
(
string_message
,
purpose:
"shipping"
),
purpose:
"shipping"
)
array_message
=
[
"credit_card_no: 5012-6748-9087-5678"
,
{
"card_holder"
=>
"Donald"
,
"issued_on"
=>
Time
.
local
(
2017
)
},
12345
]
assert_equal
array_message
,
parse
(
generate
(
array_message
,
purpose:
"registration"
),
purpose:
"registration"
)
end
def
test_encryption_and_decryption_with_different_purposes_returns_nil
assert_nil
parse
(
generate
(
@message
,
purpose:
"payment"
),
purpose:
"sign up"
)
assert_nil
parse
(
generate
(
@message
,
purpose:
"payment"
))
assert_nil
parse
(
generate
(
@message
),
purpose:
"sign up"
)
assert_nil
parse
(
generate
(
@message
),
purpose:
""
)
end
def
test_purpose_using_symbols
assert_equal
@message
,
parse
(
generate
(
@message
,
purpose: :checkout
),
purpose: :checkout
)
assert_equal
@message
,
parse
(
generate
(
@message
,
purpose: :checkout
),
purpose:
"checkout"
)
assert_equal
@message
,
parse
(
generate
(
@message
,
purpose:
"checkout"
),
purpose: :checkout
)
end
def
test_passing_expires_at_sets_expiration_date
encrypted_message
=
generate
(
@message
,
expires_at:
1
.
hour
.
from_now
)
travel
59
.
minutes
assert_equal
@message
,
parse
(
encrypted_message
)
travel
2
.
minutes
assert_nil
parse
(
encrypted_message
)
end
def
test_set_relative_expiration_date_by_passing_expires_in
encrypted_message
=
generate
(
@message
,
expires_in:
2
.
hours
)
travel
1
.
hour
assert_equal
@message
,
parse
(
encrypted_message
)
travel
1
.
hour
+
1
.
second
assert_nil
parse
(
encrypted_message
)
end
def
test_passing_expires_in_less_than_a_second_is_not_expired
freeze_time
do
encrypted_message
=
generate
(
@message
,
expires_in:
1
.
second
)
travel
0.5
.
seconds
assert_equal
@message
,
parse
(
encrypted_message
)
travel
1
.
second
assert_nil
parse
(
encrypted_message
)
end
end
def
test_favor_expires_at_over_expires_in
payment_related_message
=
generate
(
@message
,
purpose:
"payment"
,
expires_at:
2
.
year
.
from_now
,
expires_in:
1
.
second
)
travel
1
.
year
assert_equal
@message
,
parse
(
payment_related_message
,
purpose: :payment
)
travel
1
.
year
+
1
.
day
assert_nil
parse
(
payment_related_message
,
purpose:
"payment"
)
end
def
test_skip_expires_at_and_expires_in_to_disable_expiration_check
payment_related_message
=
generate
(
@message
,
purpose:
"payment"
)
travel
100
.
years
assert_equal
@message
,
parse
(
payment_related_message
,
purpose:
"payment"
)
end
end
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录