• HOME
  • SERVICES
    • DATABASE SERVICES
    • CYBERSECURITY
    • INTERNET OF THINGS
  • PUBLISHING
    • PORTFOLIO
    • ORACLE SCRIPT LIBRARY
    • R CODE SCRIPT LIBRARY
  • ABOUT
    • ABOUT MONTBLEAU.CA
    • About Pierre Montbleau
  • Blog
  • Academic Press
    • Bookshelf

10-DB_Security_Risks.r

# -----------------------------------------------------------------------------------# File Name : 10-DB_Security_Risks.r# Author : Pierre Montbleau# Description : Advanced security risk analysis for Oracle databases using anomaly # detection, behavioral clustering, and pattern analysis to identify # potentially malicious user activities and security vulnerabilities# Purpose : Proactive security monitoring, risk assessment, and threat detection# Call Syntax : source("F:\\DBA\\Scripts\\R\\10-DB_Security_Risks.r")# Parameters : None - all database connection parameters are hardcoded within script# Last Modified: 2025-12-17# -----------------------------------------------------------------------------------
# -----------------------------------------------------------------------------------# Load Required Libraries# -----------------------------------------------------------------------------------library(DBI) # Database Interfacelibrary(odbc) # ODBC Database Connectivitylibrary(dplyr) # Data Manipulationlibrary(lubridate) # Date-Time Processinglibrary(ggplot2) # Data Visualizationlibrary(anomalize) # Anomaly Detectionlibrary(cluster) # Clustering Algorithmslibrary(tidyr) # Data Reshapinglibrary(config) # Configuration Management
# -----------------------------------------------------------------------------------# Function: safe_db_connect# Purpose : Secure database connection with error handling# Returns : Connection object on success, NULL on failure# Security: Implements secure connection practices with proper error handling# -----------------------------------------------------------------------------------safe_db_connect <- function() { tryCatch({ conn <- dbConnect(odbc::odbc(), dsn = "[DB_NAME]", uid = "[USERNAME]", pwd = "[PASSWORD]") message("Successfully connected to database") return(conn) }, error = function(e) { message("Connection failed: ", e$message) return(NULL) })}
# -----------------------------------------------------------------------------------# Function: analyze_security_risks# Purpose : Main analysis function that orchestrates security risk assessment# Process : 1. Connects to database# 2. Retrieves multiple security-related datasets# 3. Performs anomaly detection on login patterns# 4. Clusters users by behavioral patterns# 5. Identifies potentially risky users# 6. Generates security recommendations# -----------------------------------------------------------------------------------analyze_security_risks <- function() { # Establish secure database connection conn <- safe_db_connect() if (is.null(conn)) { stop("Failed to establish database connection. Analysis aborted.") } # Ensure connection is closed on exit on.exit({ dbDisconnect(conn) message("Database connection closed") }) # Main analysis block with error handling tryCatch({ # ------------------------------------------------------------------------------- # Data Collection Phase # Retrieves security-related data from multiple audit tables # ------------------------------------------------------------------------------- # Retrieve user activity data (last 90 days) user_activity <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, LOGIN_TIME, CLIENT_IP, CLIENT_HOSTNAME, OS_USER, ACTION, CLIENT_PROGRAM_NAME FROM ATTSTATS.USER_ACTIVITY_LOG WHERE TRUNC(LOGIN_TIME) > TRUNC(SYSDATE-90)") }, error = function(e) { warning("Failed to retrieve user activity data: ", e$message) return(NULL) }) # Retrieve audit activity data audit_activity <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, ACTION_NAME, TIMESTAMP FROM ATTSTATS.RECENT_AUDIT_ACTIVITY") }, error = function(e) { warning("Failed to retrieve audit activity data: ", e$message) return(NULL) }) # Retrieve user account information (last 90 days) user_accounts <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, ACCOUNT_STATUS, DEFAULT_TABLESPACE, PROFILE, LAST_LOGIN, ORACLE_MAINTAINED FROM ATTSTATS.USER_ACCOUNT_INFORMATION WHERE TRUNC(CHECK_DATE) > TRUNC(SYSDATE-90)") }, error = function(e) { warning("Failed to retrieve user account data: ", e$message) return(NULL) }) # Retrieve DDL (Data Definition Language) audit data ddl_audit <- tryCatch({ dbGetQuery(conn, " SELECT USERNAME, OPERATION, OBJECT_TYPE, OBJECT_NAME, OBJECT_OWNER FROM ATTSTATS.DDL_AUDIT_LOG") }, error = function(e) { warning("Failed to retrieve DDL audit data: ", e$message) return(NULL) }) # Validate minimum data requirements if (is.null(user_activity)) { stop("User activity data is required but not available. Analysis aborted.") } # ------------------------------------------------------------------------------- # Data Preprocessing Phase # Prepares user activity data for analysis # ------------------------------------------------------------------------------- activity_summary <- user_activity %>% mutate( LOGIN_DATETIME = as.POSIXct(LOGIN_TIME), # Convert to datetime LOGIN_DATE = as.Date(LOGIN_DATETIME), # Extract date component LOGIN_HOUR = hour(LOGIN_DATETIME) # Extract hour component ) %>% group_by(USERNAME, LOGIN_DATE) %>% summarise( DAILY_LOGINS = n(), # Count of daily logins UNIQUE_CLIENTS = n_distinct(CLIENT_IP), # Unique IP addresses UNIQUE_PROGRAMS = n_distinct(CLIENT_PROGRAM_NAME), # Unique client programs ACTIONS = paste(unique(ACTION), collapse = ", "), # Concatenated actions .groups = "drop" ) # ------------------------------------------------------------------------------- # Anomaly Detection Phase # Identifies unusual patterns in daily login activity # ------------------------------------------------------------------------------- login_anomalies <- activity_summary %>% as_tibble() %>% mutate(LOGIN_DATETIME = as.POSIXct(paste(LOGIN_DATE, "00:00:00"))) %>% time_decompose(DAILY_LOGINS, method = "stl", frequency = "1 week") %>% # Decompose time series anomalize(remainder, method = "gesd") %>% # Detect anomalies using GESD method time_recompose() # Reconstruct time series # ------------------------------------------------------------------------------- # Visualization: Anomaly Plot # Shows detected anomalies in login patterns # ------------------------------------------------------------------------------- anomaly_plot <- login_anomalies %>% plot_anomalies(time_recomposed = TRUE) + labs( title = "Anomalous Login Activity by User", subtitle = "Daily login patterns with detected anomalies (Red points indicate anomalies)", x = "Date", y = "Number of Logins" ) + theme_minimal() + theme(plot.title = element_text(face = "bold", size = 14), plot.subtitle = element_text(color = "darkgray")) print(anomaly_plot) # Extract suspicious users from anomaly detection suspicious_users <- login_anomalies %>% filter(anomaly == "Yes") %>% select(USERNAME, LOGIN_DATE, DAILY_LOGINS, recomposed_l1, recomposed_l2) # ------------------------------------------------------------------------------- # DDL Pattern Analysis # Analyzes structural changes to database objects # ------------------------------------------------------------------------------- ddl_patterns <- if (!is.null(ddl_audit)) { ddl_audit %>% group_by(USERNAME, OPERATION, OBJECT_TYPE) %>% summarise(COUNT = n(), .groups = "drop") %>% pivot_wider( names_from = c(OPERATION, OBJECT_TYPE), values_from = COUNT, values_fill = 0 ) } else { warning("DDL patterns analysis skipped due to missing data") tibble(USERNAME = character()) } # ------------------------------------------------------------------------------- # Behavioral Clustering Phase # Groups users by their activity patterns using k-means clustering # ------------------------------------------------------------------------------- behavior_data <- activity_summary %>% group_by(USERNAME) %>% summarise( AVG_LOGINS = mean(DAILY_LOGINS), # Average daily logins MAX_LOGINS = max(DAILY_LOGINS), # Maximum daily logins CLIENT_VARIETY = mean(UNIQUE_CLIENTS), # Average unique clients PROGRAM_VARIETY = mean(UNIQUE_PROGRAMS), # Average unique programs .groups = "drop" ) # Enrich behavior data with DDL patterns if available if (nrow(ddl_patterns) > 0) { behavior_data <- behavior_data %>% left_join(ddl_patterns, by = "USERNAME") } # Handle missing values (replace NA with 0) behavior_data <- behavior_data %>% replace(is.na(.), 0) # ------------------------------------------------------------------------------- # Clustering Analysis # Performs k-means clustering on normalized behavioral data # ------------------------------------------------------------------------------- if (nrow(behavior_data) > 1) { # Normalize data for clustering scaled_data <- scale(select(behavior_data, -USERNAME)) # Determine optimal number of clusters (maximum of 5 or n-1) set.seed(123) # For reproducibility max_clusters <- min(5, nrow(behavior_data) - 1) kmeans_result <- kmeans(scaled_data, centers = max_clusters) behavior_data$CLUSTER <- as.factor(kmeans_result$cluster) # Analyze cluster characteristics cluster_summary <- behavior_data %>% group_by(CLUSTER) %>% summarise(across(where(is.numeric), mean), .groups = "drop") %>% arrange(desc(AVG_LOGINS)) # Identify unusual clusters (potential security risks) # Clusters with high average logins or extremely high maximum logins unusual_cluster <- cluster_summary %>% filter( AVG_LOGINS > quantile(cluster_summary$AVG_LOGINS, 0.75) | MAX_LOGINS > quantile(cluster_summary$MAX_LOGINS, 0.9) ) %>% pull(CLUSTER) # Flag users in unusual clusters as potentially risky risky_users <- behavior_data %>% filter(CLUSTER %in% unusual_cluster) # Enrich risky users with account information if available if (!is.null(user_accounts)) { risky_users <- risky_users %>% left_join(user_accounts, by = "USERNAME") %>% select( USERNAME, AVG_LOGINS, MAX_LOGINS, CLIENT_VARIETY, PROGRAM_VARIETY, ACCOUNT_STATUS, PROFILE, ORACLE_MAINTAINED, CLUSTER ) } # ----------------------------------------------------------------------------- # Visualization: Cluster Plot # Shows user clusters based on behavioral patterns # ----------------------------------------------------------------------------- cluster_plot <- ggplot(behavior_data, aes(x = AVG_LOGINS, y = MAX_LOGINS, color = CLUSTER)) + geom_point(alpha = 0.7, size = 3) + labs( title = "User Behavior Clusters for Security Analysis", subtitle = "K-means clustering based on login patterns (Colors represent behavioral groups)", x = "Average Daily Logins", y = "Maximum Daily Logins", color = "Behavior Cluster" ) + theme_minimal() + scale_color_brewer(palette = "Set1") + theme(plot.title = element_text(face = "bold", size = 14), legend.position = "bottom") print(cluster_plot) # ----------------------------------------------------------------------------- # Generate Security Recommendations # Provides actionable insights based on analysis results # ----------------------------------------------------------------------------- generate_recommendations(risky_users) } else { warning("Insufficient data for clustering analysis") cat("No user behavior patterns detected due to insufficient data\n") } }, error = function(e) { message("Analysis failed: ", e$message) })}
# -----------------------------------------------------------------------------------# Function: generate_recommendations# Purpose : Generates actionable security recommendations based on identified risks# Input : risky_users - Data frame of potentially risky users with metrics# Output : Printed recommendations to console# -----------------------------------------------------------------------------------generate_recommendations <- function(risky_users) { if (nrow(risky_users) > 0) { cat("\n" + strrep("=", 60) + "\n") cat("POTENTIALLY RISKY USERS IDENTIFIED\n") cat(strrep("=", 60) + "\n\n") print(risky_users) cat("\n" + strrep("=", 60) + "\n") cat("SECURITY RECOMMENDATIONS\n") cat(strrep("=", 60) + "\n\n") cat("1. USER ACCOUNT REVIEW:\n") cat(" - Review activity patterns and permissions for:\n") cat(" ", paste(unique(risky_users$USERNAME), collapse = ", "), "\n\n") cat("2. ENHANCED MONITORING:\n") cat(" - Implement additional monitoring for flagged accounts\n") cat(" - Set up alerts for unusual login patterns\n\n") cat("3. ACTIVITY VALIDATION:\n") cat(" - Verify if the detected high activity levels are legitimate\n") cat(" - Check business justification for observed patterns\n\n") # Special warning for Oracle-maintained accounts if ("ORACLE_MAINTAINED" %in% colnames(risky_users)) { oracle_maintained <- filter(risky_users, ORACLE_MAINTAINED == "Y") if (nrow(oracle_maintained) > 0) { cat("4. CRITICAL WARNING - ORACLE-MAINTAINED ACCOUNTS:\n") cat(" - The following Oracle-maintained accounts show risky patterns:\n") cat(" ", paste(unique(oracle_maintained$USERNAME), collapse = ", "), "\n") cat(" - These accounts typically have minimal activity\n") cat(" - Investigate immediately for potential security breaches\n\n") } } cat("5. NEXT STEPS:\n") cat(" - Schedule security review meeting\n") cat(" - Update monitoring policies based on findings\n") cat(" - Consider implementing multi-factor authentication\n") } else { cat("\n" + strrep("=", 60) + "\n") cat("SECURITY ASSESSMENT RESULTS\n") cat(strrep("=", 60) + "\n") cat("\nNo unusual user behavior patterns detected in this analysis period.\n") cat("Current security posture appears normal based on analyzed metrics.\n") }}
# -----------------------------------------------------------------------------------# Execute Security Risk Analysis# -----------------------------------------------------------------------------------cat("\n" + strrep("=", 60) + "\n")cat("INITIATING DATABASE SECURITY RISK ANALYSIS\n")cat(strrep("=", 60) + "\n\n")
analyze_security_risks()
cat("\n" + strrep("=", 60) + "\n")cat("SECURITY ANALYSIS COMPLETED\n")cat(strrep("=", 60) + "\n")
# -----------------------------------------------------------------------------------
montbleau.ca Academic press
address
110 Rue Reid Vaudreuil-Dorion, Qc, Canada J7V 0G1
Mon-Sun 9 am - 7 pm
contact us
montbleau@hotmail.com
5149497697
follow us/
Copyright © 2024. All rights reserved. Powered by Domain.com.

We use cookies to enable essential functionality on our website, and analyze website traffic. By clicking Accept you consent to our use of cookies. Read about how we use cookies.

Your Cookie Settings

We use cookies to enable essential functionality on our website, and analyze website traffic. Read about how we use cookies.

Cookie Categories
Essential

These cookies are strictly necessary to provide you with services available through our websites. You cannot refuse these cookies without impacting how our websites function. You can block or delete them by changing your browser settings, as described under the heading "Managing cookies" in the Privacy and Cookies Policy.

Analytics

These cookies collect information that is used in aggregate form to help us understand how our websites are being used or how effective our marketing campaigns are.