From 281d5f6ebf622841beb527caffe5697a8eaa8a8b Mon Sep 17 00:00:00 2001 From: VetheonGames Date: Sun, 11 Jun 2023 15:46:53 -0600 Subject: [PATCH] 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. --- .gitignore | 1 + bin/NETRAVE | 6 ++++-- lib/utils/database_manager.rb | 20 +++++++++--------- lib/utils/first_run_init.rb | 18 ++++++++++++----- lib/utils/logg_man.rb | 30 +++++++++++++++++++++++++++ lib/utils/utilities.rb | 38 ++++++++++++++++------------------- 6 files changed, 76 insertions(+), 37 deletions(-) create mode 100644 lib/utils/logg_man.rb diff --git a/.gitignore b/.gitignore index 499c4e8..152086c 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,4 @@ build-iPhoneSimulator/ # Used by RuboCop. Remote config files pulled in from inherit_from directive. # .rubocop-https?--* bin/config.yml +lib/netrave.log diff --git a/bin/NETRAVE b/bin/NETRAVE index d8d26cd..babc018 100644 --- a/bin/NETRAVE +++ b/bin/NETRAVE @@ -10,7 +10,7 @@ require_relative '../lib/utils/utilities' include Utilities # rubocop:disable Style/MixinUsage # 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 Dotenv.load @@ -42,7 +42,9 @@ if db_details.values.any?(&:nil?) end # 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 unless db_manager.test_db_connection(db_details) diff --git a/lib/utils/database_manager.rb b/lib/utils/database_manager.rb index 3c35f32..0f89bb0 100644 --- a/lib/utils/database_manager.rb +++ b/lib/utils/database_manager.rb @@ -13,18 +13,20 @@ class DatabaseManager @db = nil end - def test_db_connection(db_details) + def test_db_connection(db_details) # rubocop:disable Metrics/MethodLength # Decrypt the password before using it - decrypted_password = decrypt_string_chacha20(db_details[:password], db_details[:key]) - connection_string = "mysql2://#{db_details[:username]}:#{decrypted_password}@localhost/#{db_details[:database]}" - @db = Sequel.connect(connection_string) - # Try a simple query to test the connection - @db.run 'SELECT 1' - true + if db_details[:password] && db_details[:key] + decrypted_password = decrypt_string_chacha20(db_details[:password], db_details[:key]) + connection_string = "mysql2://#{db_details[:username]}:#{decrypted_password}@localhost/#{db_details[:database]}" + @db = Sequel.connect(connection_string) + # Try a simple query to test the connection + @db.run 'SELECT 1' + true + else + false + end rescue Sequel::DatabaseConnectionError false - ensure - @db&.disconnect end def create_system_info_table diff --git a/lib/utils/first_run_init.rb b/lib/utils/first_run_init.rb index 76258d7..71076fe 100644 --- a/lib/utils/first_run_init.rb +++ b/lib/utils/first_run_init.rb @@ -6,6 +6,7 @@ require 'dotenv' require_relative 'database_manager' require_relative 'system_information_gather' require_relative 'utilities' +require_relative 'logg_man' # first run class class FirstRunInit @@ -15,6 +16,7 @@ class FirstRunInit def initialize(db_manager = nil) @db_manager = db_manager || DatabaseManager.new @info_gatherer = SystemInformationGather.new(@db_manager) + @loggman = LoggMan.new end def run @@ -75,17 +77,23 @@ class FirstRunInit # Encrypt the password 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 def write_db_details_to_config_file(db_details) # Write the database details to the .env file File.open('.env', 'w') do |file| - file.puts "DB_USERNAME=#{db_details[:username]}" - file.puts "DB_PASSWORD=#{db_details[:password]}" - file.puts "DB_SECRET_KEY=#{db_details[:key]}" - file.puts "DB_DATABASE=#{db_details[:database]}" + file.puts %(DB_USERNAME="#{db_details[:username]}") + file.puts %(DB_PASSWORD="#{db_details[:password]}") + file.puts %(DB_SECRET_KEY="#{db_details[:key]}") + file.puts %(DB_DATABASE="#{db_details[:database]}") end + + # Load the .env file using dotenv + Dotenv.load + rescue StandardError => e + @loggman.log_error("Failed to write to .env file: #{e.message}") end def ask_for_default_mode diff --git a/lib/utils/logg_man.rb b/lib/utils/logg_man.rb new file mode 100644 index 0000000..af35426 --- /dev/null +++ b/lib/utils/logg_man.rb @@ -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 diff --git a/lib/utils/utilities.rb b/lib/utils/utilities.rb index 3f3fb6f..a31dfef 100644 --- a/lib/utils/utilities.rb +++ b/lib/utils/utilities.rb @@ -4,6 +4,7 @@ require 'securerandom' require 'digest' require 'base64' require 'openssl' +require_relative 'logg_man' # Utiltiies Module module Utilities @@ -32,31 +33,26 @@ module Utilities Base64.encode64(SecureRandom.bytes(32)).chomp end - def encrypt_string_chacha20(string, key) + def encrypt_string_chacha20(data, key) cipher = OpenSSL::Cipher.new('chacha20') cipher.encrypt - cipher.key = Base64.decode64(key) - encrypted = cipher.update(string) + cipher.final - Base64.encode64(encrypted).chomp + cipher.key = Base64.decode64(key) # Decode the key from Base64 + encrypted_data = cipher.update(data) + cipher.final + + 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 - def decrypt_string_chacha20(encrypted_string, key) - decipher = OpenSSL::Cipher.new('chacha20') - decipher.decrypt - decipher.key = Base64.decode64(key) - decrypted = Base64.decode64(encrypted_string) - decipher.update(decrypted) + decipher.final - end + def decrypt_string_chacha20(encrypted_data, key) + return nil if encrypted_data.nil? - # Encrypts a given data object using Blowfish and returns the encrypted string - def encrypt_data_blowfish(data, key) - plain_text = YAML.dump(data) - encrypt_string_blowfish(plain_text, key) - 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) + cipher = OpenSSL::Cipher.new('chacha20') + cipher.decrypt + cipher.key = Base64.decode64(key) # Decode the key from Base64 + cipher.update(Base64.decode64(encrypted_data)) + cipher.final end end