Implement Encryption for Sensitive Data and Switch to .env for Configuration Storage

This commit introduces several significant changes to improve the security and configuration management of the application.

    Encryption of Sensitive Data: We've introduced encryption for sensitive data such as database passwords. This is done using the Blowfish algorithm. The encryption and decryption methods are added to the Utilities module. The encryption is applied when the user enters their database password during the first run setup. The encrypted password is then stored in the .env file. When the application needs to use the password, it is decrypted using the key that was generated during encryption.

    Switch from YAML to .env for Configuration Storage: The application now uses a .env file for storing configuration data instead of a YAML file. This change was made to take advantage of the .env file's ability to store environment variables, which can be easily accessed by the application. The .env file stores the database username, encrypted password, encryption key, and database name.

    First Run Setup Changes: The FirstRunInit class has been updated to ask the user for their database details, encrypt the password, and store these details in the .env file. It also checks if any of the necessary details are missing and runs the first run setup if they are.

    Database Connection Testing: The DatabaseManager class now decrypts the password before using it to test the database connection. It also checks if the password or key are nil before attempting to decrypt the password.

    Logging: A new LoggMan class has been added to handle logging. This class provides a simple method for logging messages, which can be used throughout the application to log errors or other important information.

    Bug Fixes and Error Handling: Several bugs were fixed and error handling was improved. For example, a bug that caused the application to crash if the .env file was missing has been fixed. Also, the application now checks if the .env file exists and creates it if it doesn't.

These changes significantly improve the security, reliability, and usability of the application.
This commit is contained in:
VetheonGames 2023-06-11 15:46:53 -06:00
parent abc256bb42
commit 281d5f6ebf
6 changed files with 76 additions and 37 deletions

1
.gitignore vendored
View File

@ -58,3 +58,4 @@ build-iPhoneSimulator/
# Used by RuboCop. Remote config files pulled in from inherit_from directive. # Used by RuboCop. Remote config files pulled in from inherit_from directive.
# .rubocop-https?--* # .rubocop-https?--*
bin/config.yml bin/config.yml
lib/netrave.log

View File

@ -10,7 +10,7 @@ require_relative '../lib/utils/utilities'
include Utilities # rubocop:disable Style/MixinUsage include Utilities # rubocop:disable Style/MixinUsage
# Create .env file if it doesn't exist # Create .env file if it doesn't exist
File.new('.env', 'w') unless File.exist?('.env') File.open('.env', 'w') {} unless File.exist?('.env')
# Load environment variables from .env file # Load environment variables from .env file
Dotenv.load Dotenv.load
@ -42,7 +42,9 @@ if db_details.values.any?(&:nil?)
end end
# Decrypt the password # Decrypt the password
db_details[:password] = decrypt_string_chacha20(db_details[:password], db_details[:key]) if db_details[:password] && db_details[:key]
db_details[:password] = decrypt_string_chacha20(db_details[:password], db_details[:key])
end
# Test connection # Test connection
unless db_manager.test_db_connection(db_details) unless db_manager.test_db_connection(db_details)

View File

@ -13,18 +13,20 @@ class DatabaseManager
@db = nil @db = nil
end end
def test_db_connection(db_details) def test_db_connection(db_details) # rubocop:disable Metrics/MethodLength
# Decrypt the password before using it # Decrypt the password before using it
decrypted_password = decrypt_string_chacha20(db_details[:password], db_details[:key]) if db_details[:password] && db_details[:key]
connection_string = "mysql2://#{db_details[:username]}:#{decrypted_password}@localhost/#{db_details[:database]}" decrypted_password = decrypt_string_chacha20(db_details[:password], db_details[:key])
@db = Sequel.connect(connection_string) connection_string = "mysql2://#{db_details[:username]}:#{decrypted_password}@localhost/#{db_details[:database]}"
# Try a simple query to test the connection @db = Sequel.connect(connection_string)
@db.run 'SELECT 1' # Try a simple query to test the connection
true @db.run 'SELECT 1'
true
else
false
end
rescue Sequel::DatabaseConnectionError rescue Sequel::DatabaseConnectionError
false false
ensure
@db&.disconnect
end end
def create_system_info_table def create_system_info_table

View File

@ -6,6 +6,7 @@ require 'dotenv'
require_relative 'database_manager' require_relative 'database_manager'
require_relative 'system_information_gather' require_relative 'system_information_gather'
require_relative 'utilities' require_relative 'utilities'
require_relative 'logg_man'
# first run class # first run class
class FirstRunInit class FirstRunInit
@ -15,6 +16,7 @@ class FirstRunInit
def initialize(db_manager = nil) def initialize(db_manager = nil)
@db_manager = db_manager || DatabaseManager.new @db_manager = db_manager || DatabaseManager.new
@info_gatherer = SystemInformationGather.new(@db_manager) @info_gatherer = SystemInformationGather.new(@db_manager)
@loggman = LoggMan.new
end end
def run def run
@ -75,17 +77,23 @@ class FirstRunInit
# Encrypt the password # Encrypt the password
encrypted_password = encrypt_string_chacha20(password, key) encrypted_password = encrypt_string_chacha20(password, key)
{ username:, password: encrypted_password, key:, database: } db_details = { username:, password: encrypted_password, key:, database: }
write_db_details_to_config_file(db_details)
end end
def write_db_details_to_config_file(db_details) def write_db_details_to_config_file(db_details)
# Write the database details to the .env file # Write the database details to the .env file
File.open('.env', 'w') do |file| File.open('.env', 'w') do |file|
file.puts "DB_USERNAME=#{db_details[:username]}" file.puts %(DB_USERNAME="#{db_details[:username]}")
file.puts "DB_PASSWORD=#{db_details[:password]}" file.puts %(DB_PASSWORD="#{db_details[:password]}")
file.puts "DB_SECRET_KEY=#{db_details[:key]}" file.puts %(DB_SECRET_KEY="#{db_details[:key]}")
file.puts "DB_DATABASE=#{db_details[:database]}" file.puts %(DB_DATABASE="#{db_details[:database]}")
end end
# Load the .env file using dotenv
Dotenv.load
rescue StandardError => e
@loggman.log_error("Failed to write to .env file: #{e.message}")
end end
def ask_for_default_mode def ask_for_default_mode

30
lib/utils/logg_man.rb Normal file
View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
require 'logger'
# LoggMan class for handling logs
class LoggMan
def initialize
@logger = Logger.new('netrave.log')
end
def log_info(message)
@logger.info(message)
end
def log_error(message)
@logger.error(message)
end
def log_warn(message)
@logger.warn(message)
end
def log_debug(message)
@logger.debug(message)
end
def log_fatal(message)
@logger.fatal(message)
end
end

View File

@ -4,6 +4,7 @@ require 'securerandom'
require 'digest' require 'digest'
require 'base64' require 'base64'
require 'openssl' require 'openssl'
require_relative 'logg_man'
# Utiltiies Module # Utiltiies Module
module Utilities module Utilities
@ -32,31 +33,26 @@ module Utilities
Base64.encode64(SecureRandom.bytes(32)).chomp Base64.encode64(SecureRandom.bytes(32)).chomp
end end
def encrypt_string_chacha20(string, key) def encrypt_string_chacha20(data, key)
cipher = OpenSSL::Cipher.new('chacha20') cipher = OpenSSL::Cipher.new('chacha20')
cipher.encrypt cipher.encrypt
cipher.key = Base64.decode64(key) cipher.key = Base64.decode64(key) # Decode the key from Base64
encrypted = cipher.update(string) + cipher.final encrypted_data = cipher.update(data) + cipher.final
Base64.encode64(encrypted).chomp
loggman = LoggMan.new
loggman.log_debug("Data to be encrypted: #{data}")
loggman.log_debug("Key: #{key}")
loggman.log_debug("Encrypted data: #{encrypted_data}")
Base64.encode64(encrypted_data).chomp
end end
def decrypt_string_chacha20(encrypted_string, key) def decrypt_string_chacha20(encrypted_data, key)
decipher = OpenSSL::Cipher.new('chacha20') return nil if encrypted_data.nil?
decipher.decrypt
decipher.key = Base64.decode64(key)
decrypted = Base64.decode64(encrypted_string)
decipher.update(decrypted) + decipher.final
end
# Encrypts a given data object using Blowfish and returns the encrypted string cipher = OpenSSL::Cipher.new('chacha20')
def encrypt_data_blowfish(data, key) cipher.decrypt
plain_text = YAML.dump(data) cipher.key = Base64.decode64(key) # Decode the key from Base64
encrypt_string_blowfish(plain_text, key) cipher.update(Base64.decode64(encrypted_data)) + cipher.final
end
# Decrypts a given encrypted string using Blowfish and returns the original data object
def decrypt_data_blowfish(encrypted_text, key)
plain_text = decrypt_string_blowfish(encrypted_text, key)
YAML.load(plain_text)
end end
end end