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.
## ── 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
## Warning: pacote 'patchwork' foi compilado no R versão 4.5.2
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:
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.
## [1] "O número de linhas é: 1194464"
## [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 :
##
## Tipos de Variáveis:
## 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,…
Primeiro vamos verificar se há dados vazios :
## [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).
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.
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)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)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:
## # 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
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.