Cách đơn giản để mã hóa thuộc tính của model trong Rails
Giới thiệu Khi lưu trữ project trên các public repo việc để các dữ liệu ở plain text thì mình thấy không an tâm lắm, nên mình sẽ tìm cách mã hóa chúng. Trong bài viết này, mình xin giới thiệu cách sử dụng module Cipher của thư viện openssl được cung cấp mặc định trong Ruby để mã hóa các dữ liệu ...
Giới thiệu
Khi lưu trữ project trên các public repo việc để các dữ liệu ở plain text thì mình thấy không an tâm lắm, nên mình sẽ tìm cách mã hóa chúng. Trong bài viết này, mình xin giới thiệu cách sử dụng module Cipher của thư viện openssl được cung cấp mặc định trong Ruby để mã hóa các dữ liệu khi lưu xuống database.
Cách cài đặt
Ở đây mình sử dụng module Cipher của thư viện OpenSSL cung cấp mặc định của Ruby nên cũng không cần cài đặt gì thêm
Cách sử dụng
Ví dụ mình có một model Staff gồm các thông tin name, email, staff_code.
create_table :staffs do |t| t.string :email, t.string :name, t.staff :code end
Đây là dữ liệu nhạy cảm nên mình muốn mã hóa chúng trước khi lưu xuống csdl. Đầu tiên tạo file lib/crypt.rb như sau:
module Crypt class << self ALGO = "aes-128-cbc" def encrypt value crypt :encrypt, value end def decrypt value crypt :decrypt, value end def encryption_key ENV["ENCRYPTION_KEY"].to_s end def crypt cipher_method, value cipher = OpenSSL::Cipher.new ALGO cipher.send cipher_method cipher.pkcs5_keyivgen encryption_key result = cipher.update value result << cipher.final end end end
Ở đây file crypt.rb là file dùng để mã hóa và giải mã. Cụ thể như sau:
- ALGO = "aes-128-cbc" : Khai báo constain kểu của mã hóa aes (Advanced Encryption Standard), độ dài 128 bit, AES thường hoạt động ở bốn chế độ cơ bản của mã khối n-bit (ECB, CBC, CFB và OFB) đặc tả bởi tiêu chuẩn ISO/IEC 10116:1997 Information technology– Security techniques – Modes of operation for an n-bit cipher (Công nghệ thông tin- kỹ thuật an toàn- chế độ hoạt động của mã hóa nbit).
- encrypt(value) là phương thức mã hóa một plain text truyền vào và trả kết quả là một chuỗi đã được mã hóa(Cipher text).
- decrypt(value) là phương thức giải mã một chuỗi truyền vào thành plain text.
- encryption_key la phương thức lấy key dùng để mã hóa cũng như giải mã. Để lấy được key này chúng ta cần phải khai báo một environtment variable ENCRYPTION_KEY
- crypt(cipher_method, value) đây là phương thức chính dùng để mã hóa cũng như giải mã.
Vậy là chúng ta đã có một module để mã hóa và giải mã.
Công việc tiếp theo là tạo file lib/encrypted_coder.rb như sau:
class EncryptedCoder def load value return if value.blank? Marshal.load Crypt.decrypt(Base64.decode64(value)) end def dump value return if value.blank? Base64.encode64 Crypt.encrypt(Marshal.dump(value)) end end
Ở đây có 2 phương thức là load và dump mình ý nghĩa tên phương thức ở phần dưới.
- load dùng để lấy kết quả từ một chuỗi đã được mã hóa.
- 'dump' dủng để mã hóa một chuỗi đầu vào. Để tăng tính bảo mật thì trước khi mã hóa chuỗi đầu vào, chúng ta dùng Marshal.dump(value) tức là dùng module Marshal của ruby để đưa 1 object thành luồng byte. Và Base64.encode64 Crypt.encrypt(Marshal.dump(value)) chuyển thành base64 chuỗi đã được mã hóa. Khi muốn giải mã thì chúng ta chỉ cần làm ngược lại theo trình tự đã làm.
Công việc tiếp theo là đến model Staff.
class Staff < ApplicationRecord serialize :name, EncryptedCoder.new serialize :email, EncryptedCoder.new serialize :staff_code, EncryptedCoder.new end
Trong class Staff chúng ta chỉ cần khai báo serialize cho các thuộc tính cần được mã hóa là name, email và staff_code. Đến đây mình xin giải thích vì sao phải dùng phương thức load và dump trong file encrypted_coder.rb. Để dùng đượng serialize thì phải có phương thức load và dump để đọc và lưu dữ liệu vào database, nếu không có thì không dùng được.
Kết quả
staff = Staff.new name: "Nguyen Van A", staff_code: "123456", email: "nguyen.van.a@gmail.com" => #<Staff id: nil, email: "nguyen.van.a@gmail.com", staff_code: "123456", name: "Nguyen Van A"> staff.save => true Staff.find_by email: "nguyen.van.a@gmail.com" Staff Load (0.4ms) SELECT `staffs`.* FROM `staffs` WHERE `staffs`.`email` = '5h1uBwsCxoQz4G8zqb06sI69YgVyrAzQv5YMPHZaI546uj24DkW4Bi8Pp0F1 aF6O ' LIMIT 1 => #<Staff id: 94, email: "nguyen.van.a@gmail.com", staff_code: "123456", name: "Nguyen Van A" >
Đây là kết quả sau khi lưu vào database SELECT name, email, staff_code FROM staffs;
Ưu điểm
Cách này đơn giản, dễ hiểu, dễ thực hiện. Có thể search được object
Nhược điểm
Có thể không chính xác với các trường ngày tháng. Cần phải chỉnh sửa lại.
Cám ơn các bạn đã đọc bài viết của mình. Chúc các bạn làm việc thật tốt.