Segurança de Dados em IIoT usando o dataset WUSTL-IIoT-2021

Introdução

A Internet das Coisas (IoT) transformou profundamente a forma como sistemas conectados operam em ambientes domésticos, comerciais e especialmente industriais. Quando esses dispositivos são aplicados ao contexto fabril, nasce o conceito de Industrial Internet of Things (IIoT) — uma rede de sensores, atuadores, controladores lógicos, máquinas inteligentes e servidores conectados com o objetivo de automatizar processos, aumentar produtividade e reduzir custos operacionais.

Embora essa integração digital tenha potencializado a eficiência industrial, ela também ampliou a superfície de ataque dessas redes. Estimativas apontam que mais de 75% dos dispositivos industriais conectados possuem vulnerabilidades conhecidas e que o número de ataques contra sistemas de controle industrial (ICS) aumentou mais de 300% nos últimos anos (ENISA, 2020 ) e (IBM X-Force, 2022 ). Incidentes como o ataque ao oleoduto Colonial Pipeline (2021), o caso do ransomware LockerGoga (2019) e invasões a usinas de água e energia mostram como falhas de segurança em sistemas industriais podem gerar prejuízos financeiros, ambientais e até riscos à vida humana.

Diferentemente de redes convencionais, ambientes IIoT possuem características muito específicas: baixa tolerância a interrupções, necessidade de comunicação em tempo real, uso de protocolos proprietários e dispositivos com capacidade limitada de processamento. Além disso, ataques em redes industriais tendem a ser raros, difíceis de observar e com comportamento muito distinto do tráfego normal. Isso torna essencial o desenvolvimento de métodos robustos de detecção de anomalias baseados em dados reais que representem adequadamente a dinâmica de um ambiente industrial.

Nesse contexto, este trabalho utiliza o dataset público WUSTL-IIoT-2021, desenvolvido pela Washington University in St. Louis, que contém mais de 1,1 milhão de registros de tráfego realista de um testbed industrial, incluindo comunicações normais e diversos ataques como DoS, Reconnaissance, Command Injection e Backdoor. O dataset está disponível em: http://www.cse.wustl.edu/~jain/iiot2/index.html

O conjunto de dados foi construído deliberadamente de forma desbalanceada, refletindo o cenário real das indústrias, onde ataques representam menos de 8% do tráfego total (Zolanvari et al., 2021 ).

O objetivo deste estudo é realizar uma análise exploratória detalhada desse dataset a fim de compreender padrões presentes no tráfego industrial, identificar diferenças entre fluxos benignos e maliciosos e discutir como essas informações podem apoiar modelos futuros de detecção de intrusão. Ao investigar métricas como volume de pacotes, taxas de transmissão, jitter, perda de pacotes e características de tempo de conexão, buscamos revelar comportamentos anômalos que podem sinalizar tentativas de ataque.

Assim, esta análise contribui para:

compreensão profunda da estrutura e padrões do tráfego industrial;

identificação de sinais característicos de ataques raros, essenciais para sistemas de detecção de intrusões (IDS);

reforço da importância da segurança em ambientes industriais conectados, tema cada vez mais relevante para infraestrutura crítica;

oferta de insights práticos para pesquisadores e profissionais, que podem aplicar as descobertas em soluções reais de proteção cibernética.

Dessa forma, o estudo da segurança de dados em IIoT não é apenas uma demanda técnica: é uma necessidade urgente para proteger sistemas que sustentam energia, água, transporte, saúde e produção industrial moderna.

Pacotes Requeridos

# Importar pacotes
library(readr)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ purrr     1.1.0
## ✔ forcats   1.0.1     ✔ stringr   1.5.2
## ✔ ggplot2   4.0.0     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readr)
library(patchwork)
## Warning: pacote 'patchwork' foi compilado no R versão 4.5.2

Preparação dos dados

Sobre o dataset

O dataset WUSTL-IIoT-2021 contém registros de tráfego de rede coletados em um testbed industrial realista, com comunicações normais e ataques cibernéticos reais (DoS, Reconnaissance, Command Injection e Backdoor). Ele representa o comportamento típico de um ambiente IIoT moderno, onde a maioria do tráfego é normal, mas ataques esporádicos representam grande risco.

Nosso objetivo nesta etapa é compreender a estrutura dos dados, observando:

  • Quantas linhas e colunas existem
  • Quais são os tipos das variáveis
  • Se há dados faltantes
  • Quais colunas representam informações importantes
  • Que comportamento geral os dados apresentam

Isso é fundamental antes de qualquer análise mais profunda.

# Carregar dataset
data <- read_csv("C:/Users/laser/OneDrive/Documentos/CPAD_2Va/wustl_iiot_2021.csv")
## Rows: 1194464 Columns: 49
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr   (3): SrcAddr, DstAddr, Traffic
## dbl  (44): Mean, Sport, Dport, SrcPkts, DstPkts, TotPkts, DstBytes, SrcBytes...
## dttm  (2): StartTime, LastTime
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
View(data)

# Número de linhas e colunas
print(paste("O número de linhas é:", nrow(data)))
## [1] "O número de linhas é: 1194464"
print(paste("O número de colunas é:", ncol(data)))
## [1] "O número de colunas é: 49"

O dataset tem quase quase 1,2 milhão de registros e 49 colunas, então é um dataset consideravelmente grande de onde podemos tirar varias informações. Nos temos as colunas :

cat("\nTipos de Variáveis: \n")
## 
## Tipos de Variáveis:
glimpse(data)
## Rows: 1,194,464
## Columns: 49
## $ StartTime  <dttm> 2019-08-19 12:23:28, 2019-08-19 15:13:24, 2019-08-19 13:41…
## $ LastTime   <dttm> 2019-08-19 12:23:28, 2019-08-19 15:13:24, 2019-08-19 13:41…
## $ SrcAddr    <chr> "192.168.0.20", "192.168.0.20", "192.168.0.20", "209.240.23…
## $ DstAddr    <chr> "192.168.0.2", "192.168.0.2", "192.168.0.2", "192.168.0.2",…
## $ Mean       <dbl> 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0,…
## $ Sport      <dbl> 59034, 55841, 63774, 61771, 0, 20559, 61906, 42211, 62961, …
## $ Dport      <dbl> 502, 502, 502, 80, 0, 80, 502, 80, 502, 502, 502, 502, 502,…
## $ SrcPkts    <dbl> 10, 10, 10, 4, 14, 2, 6, 2, 10, 10, 10, 10, 10, 76, 10, 10,…
## $ DstPkts    <dbl> 8, 8, 8, 0, 0, 0, 6, 0, 8, 8, 8, 8, 8, 0, 8, 8, 6, 8, 8, 8,…
## $ TotPkts    <dbl> 18, 18, 18, 4, 14, 2, 12, 2, 18, 18, 18, 18, 18, 76, 18, 18…
## $ DstBytes   <dbl> 508, 508, 508, 0, 0, 0, 384, 0, 508, 508, 508, 508, 508, 0,…
## $ SrcBytes   <dbl> 644, 644, 644, 248, 868, 124, 396, 124, 644, 644, 644, 644,…
## $ TotBytes   <dbl> 1152, 1152, 1152, 248, 868, 124, 780, 124, 1152, 1152, 1152…
## $ SrcLoad    <dbl> 8.748609e+04, 8.807730e+04, 8.958739e+04, 1.672747e+03, 1.8…
## $ DstLoad    <dbl> 67122.95, 67576.55, 68735.16, 0.00, 0.00, 0.00, 86038.85, 0…
## $ Load       <dbl> 1.546090e+05, 1.556538e+05, 1.583225e+05, 1.672747e+03, 1.8…
## $ SrcRate    <dbl> 1.696929e+02, 1.708396e+02, 1.737686e+02, 3.372473e+00, 3.7…
## $ DstRate    <dbl> 131.9833, 132.8752, 135.1534, 0.0000, 0.0000, 0.0000, 168.0…
## $ Rate       <dbl> 3.205309e+02, 3.226970e+02, 3.282297e+02, 3.372473e+00, 3.7…
## $ SrcLoss    <dbl> 2, 2, 2, 3, 0, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2,…
## $ DstLoss    <dbl> 2, 2, 2, 0, 0, 0, 2, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2,…
## $ Loss       <dbl> 4, 4, 4, 3, 0, 2, 4, 2, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4,…
## $ pLoss      <dbl> 18.18182, 18.18182, 18.18182, 42.85714, 0.00000, 50.00000, …
## $ SrcJitter  <dbl> 527.431726, 17.234379, 522.987240, 419.338813, 525.146562, …
## $ DstJitter  <dbl> 11.523097, 13.246678, 12.307223, 0.000000, 0.000000, 0.0000…
## $ SIntPkt    <dbl> 5.893000, 7.525857, 5.754778, 296.518344, 321.429844, 0.000…
## $ DIntPkt    <dbl> 7.406429, 7.338714, 7.299143, 0.000000, 0.000000, 0.000000,…
## $ Proto      <dbl> 6, 6, 6, 6, 2054, 6, 6, 6, 6, 6, 6, 6, 6, 17, 6, 6, 6, 6, 6…
## $ Dur        <dbl> 0.053037, 0.052681, 0.051793, 0.889555, 3.500055, 0.000000,…
## $ TcpRtt     <dbl> 0.001266, 0.001310, 0.000766, 0.000000, 0.000000, 0.000000,…
## $ IdleTime   <dbl> 1548786176, 1548882816, 1548877312, 1548787456, 1548881408,…
## $ Sum        <dbl> 0.053037, 0.052681, 0.051793, 0.889555, 3.500055, 0.000000,…
## $ Min        <dbl> 0.053037, 0.052681, 0.051793, 0.889555, 3.500055, 0.000000,…
## $ Max        <dbl> 0.053037, 0.052681, 0.051793, 0.889555, 3.500055, 0.000000,…
## $ sDSb       <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ sTtl       <dbl> 128, 128, 128, 140, 0, 142, 128, 243, 128, 128, 128, 128, 1…
## $ dTtl       <dbl> 64, 64, 64, 0, 0, 0, 64, 0, 64, 64, 64, 64, 64, 0, 64, 64, …
## $ sIpId      <dbl> 53331, 37167, 58712, 21629, 0, 21629, 41565, 21518, 41850, …
## $ dIpId      <dbl> 64402, 31590, 22717, 0, 0, 0, 15177, 0, 35175, 30353, 41235…
## $ SAppBytes  <dbl> 24, 24, 24, 0, 476, 0, 24, 0, 24, 24, 24, 24, 24, 2432, 24,…
## $ DAppBytes  <dbl> 20, 20, 20, 0, 0, 0, 20, 0, 20, 20, 20, 20, 20, 0, 20, 20, …
## $ TotAppByte <dbl> 44, 44, 44, 0, 476, 0, 44, 0, 44, 44, 44, 44, 44, 2432, 44,…
## $ SynAck     <dbl> 0.001176, 0.001308, 0.000690, 0.000000, 0.000000, 0.000000,…
## $ RunTime    <dbl> 0.053037, 0.052681, 0.051793, 0.889555, 3.500055, 0.000000,…
## $ sTos       <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ SrcJitAct  <dbl> 0.00000, 0.00000, 0.00000, 419.33881, 525.14656, 0.00000, 0…
## $ DstJitAct  <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ Traffic    <chr> "normal", "normal", "normal", "DoS", "normal", "DoS", "norm…
## $ Target     <dbl> 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,…
  • StartTime (POSIXct) — horário em que o fluxo começou
  • LastTime (POSIXct) — horário em que o fluxo terminou
  • SrcAddr (chr) — endereço IP de origem
  • DstAddr (chr) — endereço IP de destino
  • Mean (num) — tempo médio entre pacotes no fluxo
  • Sport (num) — porta de origem
  • Dport (num) — porta de destino
  • SrcPkts (num) — pacotes enviados pela origem
  • DstPkts (num) — pacotes enviados pelo destino
  • TotPkts (num) — total de pacotes no fluxo
  • DstBytes (num) — bytes recebidos pelo destino
  • SrcBytes (num) — bytes enviados pela origem
  • TotBytes (num) — total de bytes transmitidos no fluxo
  • SrcLoad (num) — carga da origem
  • DstLoad (num) — carga do destino
  • Load (num) — carga total do fluxo
  • SrcRate (num) — taxa de pacotes por segundo da origem
  • DstRate (num) — taxa de pacotes por segundo do destino
  • Rate (num) — taxa total de pacotes por segundo
  • SrcLoss (num) — pacotes perdidos ou retransmitidos pela origem
  • DstLoss (num) — pacotes perdidos ou retransmitidos pelo destino
  • Loss (num) — perdas totais no fluxo
  • pLoss (num) — porcentagem de perda de pacotes
  • SrcJitter (num) — jitter da origem
  • DstJitter (num) — jitter do destino
  • SIntPkt (num) — tempo entre pacotes enviados pela origem
  • DIntPkt (num) — tempo entre pacotes enviados pelo destino
  • Proto (num) — código do protocolo
  • Dur (num) — duração total do fluxo
  • TcpRtt (num) — tempo de RTT no handshake TCP
  • IdleTime (num) — tempo ocioso desde o último pacote
  • Sum (num) — soma das durações agregadas
  • Min (num) — menor duração agregada
  • Max (num) — maior duração agregada
  • sDSb (num) — Differentiated Services Byte
  • sTtl (num) — TTL da origem
  • dTtl (num) — TTL do destino
  • sIpId (num) — identificador IP da origem
  • dIpId (num) — identificador IP do destino
  • SAppBytes (num) — bytes da aplicação enviados pela origem
  • DAppBytes (num) — bytes da aplicação enviados pelo destino
  • TotAppByte (num) — total de bytes da camada de aplicação
  • SynAck (num) — tempo entre SYN e SYN-ACK
  • RunTime (num) — tempo total de execução do fluxo
  • sTos (num) — Type of Service
  • SrcJitAct (num) — jitter ativo da origem
  • DstJitAct (num) — jitter ativo do destino
  • Traffic (chr) — rótulo do tráfego
  • Target (num) — classe binária (0 normal, 1 ataque)

Pré-processamento dos dados

Primeiro vamos verificar se há dados vazios :

sum(is.na(data))
## [1] 0
n_original <- nrow(data)

n_unique <- nrow(distinct(data))

n_duplicadas <- n_original - n_unique
n_duplicadas
## [1] 0

Verificamos que não há colunas vazias nem dados duplicados.

Para atender aos objetivos criamos novas variáveis baseadas em tempo e no rótulo de tráfego.

data_processed <- data %>%
  mutate(
    StartTime = as_datetime(StartTime), # Garantindo formato de data/hora
    Hora_Ataque = hour(StartTime),
    Dia_Semana = wday(StartTime, label = TRUE, abbr = TRUE, week_start = 1), # Dia da semana (Segunda é 1)
    
    # Criação da variável de Rótulo Único (Normal ou Tipo de Ataque)
    Traffic_Type = ifelse(Target == 1, Traffic, "Normal"),
    
    # Tratamento de Assimetria: Transformação Logarítmica para Duração e Taxa
    Log_Dur = log(Dur + 0.00001),
    Log_Rate = log(Rate + 0.00001)
  )

cat("\nNovas Colunas Criadas (Amostra):\n")
## 
## Novas Colunas Criadas (Amostra):
print(head(data_processed %>% select(StartTime, Hora_Ataque, Dia_Semana, Traffic_Type, Log_Dur, Log_Rate)))
## # A tibble: 6 × 6
##   StartTime           Hora_Ataque Dia_Semana Traffic_Type Log_Dur Log_Rate
##   <dttm>                    <int> <ord>      <chr>          <dbl>    <dbl>
## 1 2019-08-19 12:23:28          12 seg        Normal        -2.94      5.77
## 2 2019-08-19 15:13:24          15 seg        Normal        -2.94      5.78
## 3 2019-08-19 13:41:31          13 seg        Normal        -2.96      5.79
## 4 2019-08-19 12:43:19          12 seg        DoS           -0.117     1.22
## 5 2019-08-19 14:49:44          14 seg        Normal         1.25      1.31
## 6 2019-08-19 13:06:07          13 seg        DoS          -11.5     -11.5

Removemos colunas redundantes, de alta cardinalidade e identificadores sem relevância preditiva direta para simplificar a análise.

cols_to_drop <- c(
  "StartTime", "LastTime",   # Substituídas por Hora_Ataque e Dia_Semana
  "TotPkts", "TotBytes",     # Redundantes (são a soma das colunas Src/Dst)
  "SrcAddr", "DstAddr",      # Alta cardinalidade (IPs)
  "sIpId", "dIpId"           # Identificadores de pacote
)

data_final_aed <- data_processed %>% 
  select(-one_of(cols_to_drop))

cat("\nNúmero de colunas no dataset após a filtragem:", ncol(data_final_aed))
## 
## Número de colunas no dataset após a filtragem: 46
cat("\nAs colunas essenciais para a AED e modelagem foram mantidas (ex: Traffic_Type, Log_Dur, Rate, Loss).")
## 
## As colunas essenciais para a AED e modelagem foram mantidas (ex: Traffic_Type, Log_Dur, Rate, Loss).

Analise exploratoria dos Dados

Distribuição da Classe (Target)

O dataset é desbalanceado, com a maioria dos registros sendo tráfego normal (Target = 0).

# Análise de desbalanceamento
distribuicao_target <- data_processed %>%
  mutate(Target_Label = ifelse(Target == 0, "Normal", "Ataque")) %>%
  group_by(Target_Label) %>%
  summarise(
    Contagem = n(),
    Porcentagem = n() / nrow(data_processed) * 100
  )

print(distribuicao_target)
## # A tibble: 2 × 3
##   Target_Label Contagem Porcentagem
##   <chr>           <int>       <dbl>
## 1 Ataque          87016        7.28
## 2 Normal        1107448       92.7
# Visualização da Distribuição da Classe
ggplot(distribuicao_target, aes(x = Target_Label, y = Contagem, fill = Target_Label)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = paste0(round(Porcentagem, 2), "%")), vjust = -0.5) +
  labs(title = "Distribuição do Tráfego: Normal vs. Ataque", x = "Tipo de Tráfego", y = "Contagem de Fluxos") +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal()

Apenas r round(distribuicao_target\(Porcentagem[distribuicao_target\)Target_Label == “Ataque”], 2)% do tráfego representa ataques, confirmando o cenário industrial real de alto desbalanceamento.

Padrãoes de ataque por hora e dia

Analisamos a distribuição temporal dos ataques (Target = 1) usando as features criadas.

# Gráfico de Ataques por Hora do Dia
ataques_por_hora_plot <- data_processed %>%
  filter(Target == 1) %>%
  ggplot(aes(x = Hora_Ataque, fill = Traffic_Type)) +
  geom_bar(position = "stack") +
  labs(title = "Contagem de Ataques por Hora do Dia", x = "Hora do Dia (0-23)", y = "Número de Ataques", fill = "Tipo de Ataque") +
  scale_x_continuous(breaks = seq(0, 23, 2)) +
  theme_minimal()

# Gráfico de Ataques por Dia da Semana
ataques_por_dia_plot <- data_processed %>%
  filter(Target == 1) %>%
  ggplot(aes(x = Dia_Semana, fill = Traffic_Type)) +
  geom_bar(position = "stack") +
  labs(title = "Contagem de Ataques por Dia da Semana", x = "Dia da Semana", y = "Número de Ataques", fill = "Tipo de Ataque") +
  theme_minimal()

# Mostrando os gráficos lado a lado
print(ataques_por_hora_plot + ataques_por_dia_plot)

Características Chave para Identificação de Ataques (Taxa, Duração e Perda)

As características mais importantes para diferenciar tráfego Normal e Malicioso são Taxa de Pacotes (Rate), Duração (Dur) e Perda de Pacotes (Loss). Analisamos como o comportamento dessas métricas se distingue entre os tipos de tráfego.

# Boxplot da Duração Logarítmica por Tipo de Tráfego
dur_plot <- ggplot(data_processed, 
       aes(x = Traffic_Type, y = Log_Dur, fill = Traffic_Type)) +
  geom_boxplot(outlier.shape = NA) + # Oculta outliers extremos para melhor visualização
  labs(title = "Duração Logarítmica dos Fluxos", 
       y = "Log(Duração)", 
       x = "Tipo de Tráfego") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "none")

# Boxplot da Taxa Logarítmica por Tipo de Tráfego
rate_plot <- ggplot(data_processed, 
       aes(x = Traffic_Type, y = Log_Rate, fill = Traffic_Type)) +
  geom_boxplot(outlier.shape = NA) +
  labs(title = "Taxa Logarítmica dos Fluxos (Rate)", 
       y = "Log(Taxa p/segundo)", 
       x = "Tipo de Tráfego") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "none")

# Combinando os gráficos
print(dur_plot + rate_plot)

  • Duração (Dur): Fluxos Normais e Backdoor tendem a ter durações médias mais longas, indicando sessões persistentes. Ataques de Reconnaissance e DoS exibem durações extremamente curtas (próximas de 0), um forte sinal de anomalia.
  • Taxa (Rate): Ataques de DoS mostram uma taxa de pacotes significativamente mais alta do que o tráfego normal, indicando a característica de inundação (flooding) desses ataques.

A perda de pacotes também é um forte indicador de ataque, pois o tráfego malicioso muitas vezes não se importa com a integridade da conexão.

# Criando uma variável binária para a ocorrência de perda
data_processed <- data_processed %>%
  mutate(Teve_Perda = ifelse(Loss > 0, 1, 0))

# Contagem de fluxo com perda (Loss > 0) por Tipo de Tráfego
loss_summary <- data_processed %>%
  group_by(Traffic_Type) %>%
  summarise(
    Total = n(),
    Com_Perda = sum(Teve_Perda),
    Pct_Com_Perda = (Com_Perda / Total) * 100
  ) %>%
  arrange(desc(Pct_Com_Perda))

cat("\nPorcentagem de Fluxos com Perda (Loss > 0) por Tipo:\n")
## 
## Porcentagem de Fluxos com Perda (Loss > 0) por Tipo:
print(loss_summary)
## # A tibble: 5 × 4
##   Traffic_Type   Total Com_Perda Pct_Com_Perda
##   <chr>          <int>     <dbl>         <dbl>
## 1 Reconn          8240      8238         100.0
## 2 Normal       1107448   1084003          97.9
## 3 CommInj          259       220          84.9
## 4 DoS            78305     60155          76.8
## 5 Backdoor         212       147          69.3

Conclusão

O presente estudo realizou uma Análise Exploratória Detalhada (AED) sobre o tráfego de rede do dataset WUSTL-IIoT-2021 com o objetivo de identificar padrões que possam diferenciar fluxos legítimos de ataques cibernéticos em infraestruturas industriais (IIoT).

A análise inicial confirmou um alto grau de desbalanceamento de classes, onde apenas aproximadamente 8% dos mais de 1,1 milhão de registros correspondem a ataques (DoS, Reconnaissance, Backdoor, Command Injection). Este cenário reflete a realidade industrial, mas exige estratégias específicas (como balanceamento e métricas de Recall) na etapa de Modelagem Preditiva.

A investigação temporal revelou que a atividade maliciosa no dataset não é distribuída aleatoriamente, mas concentra-se intensamente no primeiro dia útil do período de coleta, com um pico significativo na Hora 0 (meia-noite). Essa concentração temporal é dominada pelos ataques de DoS e Reconnaissance (conforme observado no Gráfico de Contagem Temporal).

Implicação: Esta descoberta permite otimizar sistemas de segurança, recomendando que a sensibilidade do monitoramento seja drasticamente aumentada em períodos específicos de maior risco.

A principal contribuição é a identificação das métricas que separam claramente os ataques do tráfego normal. Para visualizar essas distinções, aplicou-se a transformação logarítmica nas variáveis Duração e Taxa para mitigar o efeito de outliers extremos e assimetria. Os resultados demonstram que a segurança em ambientes IIoT pode ser reforçada focando em padrões comportamentais do fluxo. Fluxos normais são persistentes e de baixa taxa, enquanto ataques são caracterizados por mudanças abruptas no volume (Rate) e na longevidade (Dur).

Com o pré-processamento de dados concluído, o próximo passo crítico é a Modelagem Preditiva, utilizando as features comprovadamente discriminatórias (como Log_Rate, Log_Dur, Teve_Perda e as variáveis temporais) para construir um Detector de Intrusão (IDS) robusto, capaz de identificar os ataques minoritários com alta precisão.