שיטות לזיהוי חריגות בנתוני רשת ומשתמשים.
שימוש באלגוריתמי למידת מכונה לאיתור התנהגות חשודה בזמן אמת.
Anomaly Detection — מפגש 2 | שיטות מתקדמות לאיתור התנהגות חשודה בזמן אמת
1
במפגש זה נעמיק בטכניקות ML מתקדמות לזיהוי חריגות ברשת ובמשתמשים. נעבור משיטות בסיסיות לכלים מבוססי בינה מלאכותית של קוד פתוח.
סקירת מושגי יסוד בזיהוי חריגות ורשתות
Isolation Forest, Autoencoders, LSTM ועוד
PyOD, Scikit-learn, TensorFlow ו-Alibi Detect
קוד חי, דאטאסטים אמיתיים ותרגול
2

זיהוי חריגות הוא תהליך של זיהוי דפוסים חריגים בנתוני רשת שאינם תואמים את ההתנהגות הצפויה. בניגוד לחתימות מסורתיות, ML לומד את "הנורמה" ומתריע על כל סטייה משמעותית.
3

דמיינו שאתם בודקים את חשבון הבנק שלכם. כל יום אתם מוציאים בין 50-200 ש"ח. פתאום יום אחד — 15,000 ש"ח. זו חריגה!
ברשת מחשבים זה בדיוק אותו דבר — מחפשים פעולות שחורגות מהדפוס הרגיל.
פעילות יומיומית, דפוסים מוכרים.
סטייה משמעותית מהנורמה, אירוע מפתיע.
הגנה מפני איומים, זיהוי פריצות מוקדם.
4

הבנת סוג החריגה קריטית לבחירת האלגוריתם הנכון. חריגת נקודה בודדת מטופלת אחרת מחריגה קולקטיבית שמצביעה על מתקפה מתואמת.
5
בואו נחדד את ההבנה שלנו בסוגי חריגות בעזרת כמה תרחישים.
חריגה נקודתית: סטייה קיצונית בנקודת זמן ספציפית.
נורמלי: פעילות רגילה בתוך דפוסי ההתנהגות הצפויים.
חריגה קונטקסטואלית: פעילות נורמלית אך בהקשר לא צפוי (זמן).
חריגה קולקטיבית: קבוצת אירועים שיוצרים דפוס חריג.
6

7

דמיינו שומר בכניסה לבניין שלמד את "הנורמה": עובדים עם תגים, בשעות קבועות, במסלולים מוכרים. כשמישהו מגיע בלי תג, בשעה 3 לפנות בוקר, ורץ לחדר השרתים — השומר מזהה חריגה!
למידת מכונה (machine learning)
עושה בדיוק את זה, רק על מיליוני אירועים בשנייה:
בניית פרופיל התנהגות רגילה.
איתור אירועים החורגים מהפרופיל.
התראה על אירועי אבטחה פוטנציאליים.
8

בחירת האלגוריתם תלויה בסוג הנתונים, נפח התעבורה ודרישות זמן האמת. נעבור על כל קטגוריה בפירוט.
9

לפני שנצלול לאלגוריתמים — נבין את הבסיס הסטטיסטי שעליו הם בנויים.
הבסיס לכל ניתוח סטטיסטי. מגדיר מה "נורמלי" ומה חריג.
(Mean & Standard Deviation)
עקומת הפעמון: רוב הנתונים מתרכזים סביב הממוצע, וחריגים נמצאים בקצוות.
(Normal Distribution)
מדד המציין כמה סטיות תקן נקודה מרוחקת מהממוצע. ערך מעל 3 מסמן חריגה.
(Z-Score)
שיטה עמידה לזיהוי חריגות שאינה מניחה התפלגות נורמלית. מתאימה לנתונים מוטים.
(IQR & Percentiles)
10
נתון הוא מדידה אחת של תכונה מסוימת. לדוגמה, גובה של אדם, ציון במבחן או זמן תגובה של שרת.
דאטאסט (Dataset) הוא אוסף של מדידות רבות — מיליוני חבילות רשת, טמפרטורות יומיות או רשומות מכירה.
ערכים כמותיים ניתנים למדידה.
דוגמה: נפח תעבורה, מספר חיבורים.
ערכים איכותיים המייצגים קטגוריות.
דוגמה: סוג פרוטוקול, מערכת הפעלה.
ערכים הקשורים לזמן.
דוגמה: חותמת זמן של אירוע, משך חיבור.
כל חבילת רשת הנכנסת או היוצאת מהארגון נחשבת לנתון בודד. מיליוני חבילות יוצרות דאטאסט רחב שבו ML יכול לזהות חריגות.
11
לפני שנוכל לזהות חריגות, עלינו להבין מה נחשב ל"נורמלי" מבחינה סטטיסטית. נתחיל עם אבני הבניין הסטטיסטיים.
הערך האופייני והמרכזי של קבוצת נתונים. מחשבים אותו כסכום כל הערכים חלקי מספרם. הממוצע נותן לנו נקודת ייחוס בודדת לתיאור הנתונים.
מדד לפיזור הנתונים סביב הממוצע. ככל שסטיית התקן () גדולה יותר, כך הנתונים מפוזרים ורחוקים יותר מהמרכז.
דמיינו כיתה עם ממוצע גובה של 170 ס"מ וסטיית תקן של 10 ס"מ. רוב התלמידים יהיו בין 160 ל־180 ס"מ. אדם בגובה 220 ס"מ יבלוט מיד כחריג!
ממוצע חבילות לשנייה ברשת הוא 1,000, עם סטיית תקן של 200. לפתע המערכת מדווחת על 5,000 חבילות לשנייה — חריגה ברורה, כיוון שהיא מרוחקת מאוד מהממוצע.
הבנת הממוצע () וסטיית התקן () היא קריטית לקביעת ספים וזיהוי אירועים חריגים.
12

עקומת הפעמון (עקומת גאוס) מתארת איך רוב הנתונים מתרכזים סביב הממוצע, ורק אחוז קטן נמצא בקצוות. זהו הבסיס להבנת 'מה נורמלי' סטטיסטית. ככל שנתון רחוק יותר מהממוצע, כך הוא חריג יותר.
מהממוצע
מהממוצע
מהממוצע
דוגמה: זמן תגובה של שרת
רוב בקשות השרת נענות בתוך 50-150 מילישניות. בקשה שלוקחת מעל 500 מילישניות היא חריגה קיצונית, כיוון שהיא נופלת מחוץ ל-3 סטיות תקן מהנורמה.
13

לא כל הנתונים מתנהגים לפי התפלגות נורמלית. במקרים אלו, אחוזונים וטווח בין-רבעוני (IQR) מספקים דרך חזקה לזהות חריגות מבלי להניח נורמליות.
אחוזון מייצג את הערך שמתחתיו נמצא אחוז מסוים מהנתונים.
14

ה-IQR הוא טווח המכיל את 50% האמצעיים של הנתונים, בין Q1 ל-Q3. הוא פחות רגיש לערכים חריגים מאשר סטיית תקן, ולכן אידיאלי כאשר הנתונים אינם מתפלגים נורמלית.
IQR = Q3 − Q1
חריג אם: x < Q1 − 1.5 × IQR או x > Q3 + 1.5 × IQR
בכיתה מסוימת, אם Q1 = 60 ו-Q3 = 85, אז ה-IQR הוא 25. ציון של 10 (מחוץ לטווח Q1 − 1.5 × IQR) או ציון של 130 (מחוץ לטווח Q3 + 1.5 × IQR) ייחשבו לחריגות סטטיסטיות.
בהעברות קבצים, ייתכן ש-Q1 = 10KB ו-Q3 = 10MB. קובץ בגודל 500MB ייחשב לחריגה המצריכה בדיקה. השיטה עמידה בפני ערכים קיצוניים המעוותים ממוצעים.
15

קורלציה (Correlation) מודדת את העוצמה והכיוון של הקשר הלינארי בין שני משתנים. היא עוזרת לנו להבין איך שינוי במשתנה אחד משפיע על שינוי במשתנה אחר.
הערך שלה נע בין (קורלציה שלילית מושלמת) ל- (קורלציה חיובית מושלמת), כאשר מצביע על חוסר קשר לינארי.
כשאחד עולה, השני עולה (או כשאחד יורד, השני יורד). לדוגמה: יותר משתמשים פעילים באתר = יותר תעבורת רשת.
כשאחד עולה, השני יורד (או כשאחד יורד, השני עולה). לדוגמה: יותר עומס על השרת = פחות מהירות תגובה.
אם הקורלציה הרגילה נשברת, זו חריגה. לדוגמה: תעבורה גבוהה אך מעט משתמשים = מתקפת DDoS פוטנציאלית.
16
בואו נראה איך חריגים יכולים להשפיע על המדדים הסטטיסטיים. נתבונן בזמני תגובה של שרת במילישניות:
[120, 130, 115, 125, 118, 122, 800, 119, 121, 117]
תשובה: כ-188.7 מילישניות (לאחר חישוב: (120+130+115+125+118+122+800+119+121+117) / 10)
תשובה: 800 מילישניות. הוא רחוק משמעותית מרוב שאר הנתונים.
תשובה: הערך החריג (800ms) מושך את הממוצע כלפי מעלה, ובכך גורם לו לא לייצג נאמנה את מרבית הנתונים ה"נורמליים".
מסקנה: במקרים בהם יש חריגים, מדדים כמו ה-IQR עדיפים על הממוצע לייצוג מרכז הנתונים וזיהוי חריגות, כיוון שהם פחות רגישים אליהם.
17
לפני כל חישוב — תמיד תסתכלו על הנתונים!
מראה כמה פעמים כל ערך מופיע (מצוין לראות התפלגות).
מראה ממוצע, רבעונים וחריגים בבת אחת.
מראה שינויים לאורך זמן.
דוגמה מרשת: היסטוגרמה של גדלי חבילות — חריגה = עמודה בודדת רחוקה מהשאר.
18
הסתברות מודדת כמה סביר שמשהו יקרה. הערך שלה נע בין 0 (בלתי אפשרי) ל-1 (ודאי).
דוגמאות פשוטות: הטלת מטבע והשגת "עץ" = 0.5 (50% סיכוי). הטלת קוביה והשגת "6" = 1/6 (כ-16.7% סיכוי).
חשיבות לזיהוי חריגות: אם הסתברות של אירוע מסוים נמוכה מאוד (למשל, 0.001) — הדבר מצביע על חריגה פוטנציאלית הדורשת בדיקה.
דוגמה מרשת: הסתברות שמשתמש יתחבר משלוש מדינות שונות באותה שעה = 0.0001
→ זוהי חריגה משמעותית המצריכה התראה!
סבירות גבוהה, חלק מהנורמה.
נמוך מספיק כדי לעורר חשד.
נדיר ביותר, כמעט בלתי אפשרי במצב רגיל.
19
מה זה: הבסיס לכל ניתוח, נקודות מידע גולמיות.
זיהוי חריגות: הבנת טבעם קריטית לבחירת כלי הזיהוי הנכון.
מה זה: מדדי מיקום ופיזור. ממוצע - מרכז, סטיית תקן - פיזור סביבו.
זיהוי חריגות: טובים לנתונים סימטריים. חריגים רחוקים מהממוצע ביותר מ-2-3 סטיות תקן.
מה זה: עקומת פעמון, רוב הנתונים קרובים לממוצע. כלל 68-95-99.7.
זיהוי חריגות: מזהה חריגים על בסיס מיקומם בעקומת הפעמון (מעבר ל-3 סטיות תקן).
מה זה: אחוזונים מחלקים נתונים, IQR הוא טווח הרבעונים האמצעי. פחות רגישים לחריגים.
זיהוי חריגות: אידיאלי לנתונים לא נורמליים. חריגים מחוץ לגבולות IQR ± 1.5 * IQR.
מה זה: מודדת קשר לינארי בין משתנים. חיובית (+1) או שלילית (-1).
זיהוי חריגות: שבירה של קשר צפוי בין משתנים, למשל: שינוי במשתנה אחד ללא שינוי מתאים בשני.
עכשיו מוכנים לאלגוריתמים! 🚀
20

חיבור בין מה שלמדנו (ממוצע, סטיית תקן, IQR) לבין האלגוריתמים הסטטיסטיים לזיהוי חריגות.
לנתונים עם התפלגות נורמלית
לנתונים ללא התפלגות נורמלית
21
השיטות הסטטיסטיות הן נקודת הפתיחה הקלאסית לזיהוי חריגות. הן מניחות שנתונים נורמליים מתפלגים סביב ממוצע ידוע, וסטייה חדה מסמנת חריגה. הן פשוטות, מהירות, ומתאימות לנתוני רשת כמו גדלי חבילות, ספירות חיבורים, ועיכובים.
ה-Z-Score מחשב כמה סטיות תקן ערך נמצא מהממוצע:
z = (x − μ) / σ
כאשר μ הוא הממוצע וσ היא סטיית התקן. הנחות:
בחירת סף: סף של 3 נפוץ (99.7% מהנתונים הנורמליים בטווח), אך ניתן להחמיר ל-2.5 לרשתות רגישות.
ה-IQR (Interquartile Range) מסתמך על הרבעונים ולא על הממוצע:
IQR = Q3 − Q1
גבולות החריגה:
מתחת ל-Q1 − 1.5·IQR
או מעל Q3 + 1.5·IQR
יתרונות על פני Z-Score:
ה-Modified Z-Score משתמש ב-חציון (median) במקום ממוצע, וב-MAD (Median Absolute Deviation) במקום סטיית תקן — מה שהופך אותו לעמיד הרבה יותר בנוכחות ערכים קיצוניים:
Modified Z = 0.6745 * (x − median) / MAD
ערכים מעל 3.5 נחשבים חריגים (Iglewicz & Hoaglin, 1993)22
import numpy as np
from scipy import stats
import pandas as pd
# Simulated network traffic: packet sizes (bytes)
np.random.seed(42)
normal_traffic = np.random.normal(loc=500, scale=50, size=1000)
# Inject anomalies (large packets, possible exfiltration)
anomalies = np.array([2000, 2500, 1800, 3000])
data = np.concatenate([normal_traffic, anomalies])
df = pd.DataFrame({'packet_size': data})
# Compute Z-Score
df['z_score'] = np.abs(stats.zscore(df['packet_size']))
# Flag anomalies with threshold = 3
threshold = 3
df['is_anomaly'] = df['z_score'] > threshold
print(f"Detected {df['is_anomaly'].sum()} anomalies")
print(df[df['is_anomaly']][['packet_size', 'z_score']])23
import numpy as np
import pandas as pd
# Simulated connection counts per second
np.random.seed(42)
normal_conn = np.random.poisson(lam=20, size=1000)
# Inject anomalies (DDoS-like spike)
anomalies = np.array([500, 750, 600, 480])
data = np.concatenate([normal_conn, anomalies])
df = pd.DataFrame({'connections': data})
# Compute IQR bounds
Q1 = df['connections'].quantile(0.25)
Q3 = df['connections'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
df['is_anomaly'] = (
(df['connections'] < lower) |
(df['connections'] > upper)
)
print(f"Q1={Q1}, Q3={Q3}, IQR={IQR}")
print(f"Bounds: [{lower:.1f}, {upper:.1f}]")
print(f"Detected {df['is_anomaly'].sum()} anomalies")24 / 70
השוואת שלוש השיטות על אותו דאטאסט:
import numpy as np
import pandas as pd
from scipy import stats
# Generate dataset with mixed anomalies
np.random.seed(0)
normal = np.random.normal(loc=300, scale=40, size=500)
anomalies = np.array([1000, 950, 1100, -200, 1200])
data = pd.Series(np.concatenate([normal, anomalies]), name='metric')
results = pd.DataFrame({'value': data})
# --- Method 1: Z-Score ---
results['z_score'] = np.abs(stats.zscore(data))
results['zscore_flag'] = results['z_score'] > 3
# --- Method 2: IQR ---
Q1, Q3 = data.quantile(0.25), data.quantile(0.75)
IQR = Q3 - Q1
results['iqr_flag'] = (data < Q1 - 1.5*IQR) | (data > Q3 + 1.5*IQR)
# --- Method 3: Modified Z-Score (robust) ---
median = np.median(data)
mad = np.median(np.abs(data - median))
results['mod_z'] = 0.6745 * np.abs(data - median) / mad
results['mod_z_flag'] = results['mod_z'] > 3.5
# Summary
print("=== Anomaly Detection Comparison ===")
print(f"Z-Score detected: {results['zscore_flag'].sum()} anomalies")
print(f"IQR detected: {results['iqr_flag'].sum()} anomalies")
print(f"Modified Z detected: {results['mod_z_flag'].sum()} anomalies")
# Show rows flagged by at least one method
flagged = results[
results['zscore_flag'] | results['iqr_flag'] | results['mod_z_flag']
]
print("\nFlagged records:")
print(flagged[['value','zscore_flag','iqr_flag','mod_z_flag']])25

Isolation Forest הוא אחד האלגוריתמים הפופולריים ביותר לזיהוי חריגות. הרעיון: נקודות חריגות קל יותר לבודד בעץ החלטה — הן דורשות פחות חיתוכים. האלגוריתם בונה יערות עצים אקראיים ומודד את "עומק הבידוד" של כל נקודה.
from sklearn.ensemble import IsolationForest
model = IsolationForest(
n_estimators=100,
contamination=0.05, # 5% anomalies expected
random_state=42
)
model.fit(X_train)
predictions = model.predict(X_test)
# -1 = anomaly, 1 = normal26
n_estimators אופטימלי
ברשתות ארגוניות
ליניארית בנפח הנתונים
27

LOF משווה את צפיפות השכנות של כל נקודה לצפיפות שכניה. נקודה עם צפיפות נמוכה בהרבה משכניה מסומנת כחריגה. אידיאלי לתעבורת רשת עם צלילים מקומיים.
from sklearn.neighbors import LocalOutlierFactor
lof = LocalOutlierFactor(
n_neighbors=20,
contamination=0.05,
novelty=False # True for predict on new data
)
labels = lof.fit_predict(X)
scores = lof.negative_outlier_factor_
# More negative = more anomalous28
Autoencoder
הוא רשת נוירונים שלומדת לדחוס לפרוס ולשחזר נתונים נורמליים.
כאשר מוזנת חריגה,
שגיאת השחזור,
(Reconstruction Error) תהיה גבוהה, מה שמסמן אותה כ
import tensorflow as tf
encoder = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(16, activation='relu')
])
decoder = tf.keras.Sequential([
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(input_dim, activation='sigmoid')
])29
pip install tensorflow numpy
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers, Model
# Build Autoencoder
input_layer = layers.Input(shape=(input_dim,))
encoded = layers.Dense(64, activation='relu')(input_layer)
encoded = layers.Dense(32, activation='relu')(encoded)
latent = layers.Dense(16, activation='relu')(encoded)
decoded = layers.Dense(32, activation='relu')(latent)
decoded = layers.Dense(64, activation='relu')(decoded)
output_layer = layers.Dense(input_dim,
activation='sigmoid')(decoded)
autoencoder = Model(input_layer, output_layer)
autoencoder.compile(optimizer='adam', loss='mse')numpy לחישובים מתמטיים, tensorflow כמסגרת למידה עמוקה, keras.layers לבניית שכבות הרשת
layers.Input(shape=(input_dim,)) — מגדיר את מספר הפיצ'רים שנכנסים לרשת
שלוש שכבות דחיסה. כל שכבה מצמצמת את הממדים. שכבת ה-Latent (16) היא "צוואר הבקבוק"
שכבות הפענוח — מנסות לשחזר את הקלט המקורי מהייצוג הדחוס
optimizer=adam: אדפטיבי ומהיר. loss=mse: מודד שגיאת שחזור בריבוע
30
pip install tensorflow numpy
# Train ONLY on normal traffic
autoencoder.fit(X_normal, X_normal,
epochs=50, batch_size=256,
validation_split=0.1)
# Detect anomalies — compute reconstruction error
reconstructions = autoencoder.predict(X_test)
mse = np.mean(np.power(X_test - reconstructions, 2), axis=1)
# Set threshold at 95th percentile of training errors
threshold = np.percentile(mse, 95)
anomalies = mse > thresholdמאמנים את הרשת רק על תעבורה נורמלית. הקלט והפלט זהים — הרשת לומדת לשחזר נורמליות
50 מחזורי אימון. batch_size=256 — כמה דוגמאות מעובדות בכל צעד
10% מהנתונים שמורים לבדיקת overfitting בזמן אמת
מריצים את כל נתוני הבדיקה דרך הרשת ומקבלים שחזור
מחשבים שגיאת שחזור לכל דוגמה. חריגה = שגיאה גבוהה
הסף נקבע כ-Percentile 95 — כל מה שמעליו נחשב חריגה
31

רשתות LSTM (Long Short-Term Memory) מצוינות לנתוני רצפים כמו לוגים ותעבורת רשת. הן "זוכרות" הקשרים ארוכי טווח ומזהות כאשר סדרת אירועים חורגת מהדפוס הצפוי.
pip install tensorflow numpy
from tensorflow.keras.layers import LSTM, Dense
from tensorflow.keras.models import Sequential
model = Sequential([
LSTM(64, input_shape=(timesteps, features),
return_sequences=True),
LSTM(32, return_sequences=False),
Dense(timesteps * features)
])
model.compile(optimizer='adam', loss='mae')
model.fit(X_train, X_train, epochs=30)ייבוא שכבת LSTM (זיכרון לטווח ארוך) ושכבה צפופה רגילה מ-Keras
בניית מודל רצפי — שכבות מחוברות אחת אחרי השנייה
שכבת LSTM ראשונה עם 64 תאים. input_shape: timesteps = כמה צעדי זמן אחורה, features = מספר פיצ'רים. return_sequences=True מעביר את כל הרצף לשכבה הבאה
שכבת LSTM שנייה עם 32 תאים. return_sequences=False — מחזירה רק את הפלט האחרון
שכבת פלט שמנסה לשחזר את הרצף המקורי. גודלה = timesteps × features
MAE (Mean Absolute Error) כפונקציית הפסד — עמידה יותר לערכים קיצוניים מ-MSE
אימון על נתונים נורמליים בלבד. הקלט = הפלט הצפוי — הרשת לומדת לשחזר נורמליות
32

PyOD היא ספריית Python המובילה לזיהוי חריגות עם מעל 40 אלגוריתמים תחת ממשק אחיד. מושלמת להשוואת אלגוריתמים ולאב-טיפוס מהיר.
pip install pyod
from pyod.models.iforest import IForest
from pyod.models.lof import LOF
from pyod.models.auto_encoder import AutoEncoder
from pyod.models.knn import KNN
# Unified API for all algorithms
clf = IForest(contamination=0.05)
clf.fit(X_train)
y_pred = clf.predict(X_test) # 0=normal, 1=anomaly
scores = clf.decision_function(X_test) # anomaly scorespip install pyodהתקנת PyOD — ספריית Python המובילה עם 40+ אלגוריתמי זיהוי חריגות תחת ממשק אחיד
from pyod.models.iforest import IForestייבוא Isolation Forest מ-PyOD. ניתן להחליף ל-LOF, AutoEncoder, KNN — אותו ממשק בדיוק
clf = IForest(contamination=0.05)יצירת מודל. contamination=0.05 = אנחנו מצפים ש-5% מהנתונים יהיו חריגים
clf.fit(X_train)אימון המודל על נתוני האימון. ב-PyOD כל האלגוריתמים משתמשים באותה פקודה
y_pred = clf.predict(X_test)חיזוי: מחזיר 0 = נורמלי, 1 = חריגה. אחיד לכל 40+ האלגוריתמים
scores = clf.decision_function(X_test)ציון רציף: ערך גבוה יותר = חריגה חזקה יותר. שימושי לתעדוף התראות
33 / 68
Alibi Detect של Seldon היא ספרייה ברמת ייצור לזיהוי חריגות, drift ועוד. מתאימה לצינורות MLOps מלאים עם תמיכה ב-TensorFlow ו-PyTorch.
pip install alibi-detect
from alibi_detect.od import IForest, OutlierAE
from alibi_detect.utils.saving import save_detector
# Autoencoder Outlier Detector
od = OutlierAE(threshold=0.012,
encoder_net=encoder,
decoder_net=decoder)
od.fit(X_train, epochs=30)
# Save for production
save_detector(od, './anomaly_detector')
# Predict with metadata
result = od.predict(X_test,
outlier_type='instance',
return_feature_score=True)pip install alibi-detectהתקנת Alibi Detect של Seldon — ספרייה ברמת ייצור עם תמיכה ב-TensorFlow ו-PyTorch
import IForest, OutlierAEייבוא גלאי חריגות: IForest = Isolation Forest, OutlierAE = Autoencoder-based detector
OutlierAE(threshold=0.012, ...)יצירת גלאי מבוסס Autoencoder. threshold = סף שגיאת שחזור. encoder_net ו-decoder_net = רשתות שהגדרתם מראש
od.fit(X_train, epochs=30)אימון הגלאי על נתונים נורמליים בלבד. epochs=30 = מספר מחזורי אימון
save_detector(od, './anomaly_detector')שמירת המודל לדיסק לשימוש בייצור. ניתן לטעון מאוחר יותר עם load_detector
od.predict(..., return_feature_score=True)חיזוי עם מטא-דאטה: outlier_type='instance' = בדיקה ברמת הדוגמה, return_feature_score=True = מחזיר ציון לכל פיצ'ר בנפרד
34 / 68

River היא ספריית Python ל-Online ML — למידה מתמשכת מ-stream של נתונים ללא צורך לאמן מחדש. מושלמת לניטור תעבורת רשת בזמן אמת.
pip install river
from river import anomaly
from river import preprocessing
# Half-Space Trees for streaming anomaly detection
model = (
preprocessing.StandardScaler() |
anomaly.HalfSpaceTrees(
n_trees=25,
height=15,
window_size=250,
seed=42
)
)
# Process each packet as it arrives
for packet in network_stream:
features = extract_features(packet)
score = model.score_one(features)
model.learn_one(features)
if score > 0.7:
alert(packet, score)pip install riverהתקנת River — ספריית Online ML לעיבוד stream של נתונים. לומדת מכל דוגמה בנפרד, ללא צורך לאמן מחדש.
StandardScaler() | HalfSpaceTrees(...)פייפליין: | מחבר שלבים. StandardScaler מנרמל, HalfSpaceTrees מזהה חריגות.
n_trees=25, height=15, window_size=250n_trees=25: מספר עצים. height=15: עומק כל עץ. window_size=250: כמה דוגמאות אחרונות נשמרות בזיכרון.
score = model.score_one(features)מחשב ציון חריגה לדוגמה הנוכחית לפני שמעדכן את המודל. ציון גבוה = חריגה.
model.learn_one(features)מעדכן את המודל עם הדוגמה הנוכחית. כך המודל מסתגל לשינויים בתעבורה לאורך זמן.
if score > 0.7: alert(...)סף 0.7 מסמן חריגה. ניתן לכוון — ערך גבוה יותר = פחות התראות שגויות.
35
sklearn.ensemble
הכי פופולרי, מהיר, לנתונים גבוהי ממד
sklearn.svm
טוב לנתונים קטנים, מגדיר גבול החלטה
sklearn.neighbors
מבוסס צפיפות, אידיאלי לחריגות מקומיות
sklearn.covariance
להנחת גאוסיאן, עמיד לרעשים
36

איכות הפיצ'רים קובעת את הצלחת המודל. תעבורת רשת עשירה בסיגנלים — השאלה היא אילו לחלץ.
משך חיבור, זמן בין חבילות (IAT), שעת יום, יום בשבוע
פרוטוקול, פורט, גודל חבילה, כיוון תעבורה, Flags TCP
מספר חבילות, bytes סה"כ, יחס Upload/Download, variance
ממוצע חיבורים לשעה, יעדים ייחודיים, entropy של DNS
37
לפני הזנת נתונים למודל יש לחלץ פיצ'רים מקבצי PCAP. שני הכלים הנפוצים ביותר:
כלי Java/Python לחילוץ אוטומטי של 80+ פיצ'רים מ-PCAP. מייצר CSV מוכן לאימון.
# Install
pip install cicflowmeter
# Extract features
cicflowmeter -f capture.pcap \
-c output.csvספריית Python לניתוח מעמיק של חבילות רשת. מאפשרת חילוץ מותאם אישית.
from scapy.all import rdpcap
packets = rdpcap('capture.pcap')
for pkt in packets:
if pkt.haslayer('IP'):
src = pkt['IP'].src
size = len(pkt)38
הדאטאסט הקלאסי לזיהוי חדירות. 5 מיליון רשומות, 41 פיצ'רים. טוב לתרגול בסיסי.
תעבורה מודרנית עם מתקפות DDoS, Brute Force, SQL Injection. מומלץ מאוד.
175,341 רשומות, 49 פיצ'רים, 9 סוגי מתקפות. נוצר ב-2015 בסביבה מבוקרת.
גרסה משופרת של KDD ללא כפילויות. נפוץ בהשוואות אלגוריתמים.
39
פיפליין ייצור מלא חייב לכלול כל חמשת השלבים. חסרון בכל שלב — ולו אחד — פוגע באמינות המערכת כולה.
40

Zeek (לשעבר Bro) הוא מנתח תעבורת רשת חזק במיוחד. הוא מייצר לוגים מובנים עשירים המתאימים ישירות לאימון מודלי ML.
# Zeek generates structured logs automatically:
# conn.log, http.log, dns.log, ssl.log
# Parse Zeek logs with Python
import pandas as pd
from zeekcut import ZeekCut
# Load connection log
conn_df = pd.read_csv('conn.log', sep='\t',
comment='#',
names=['ts','uid','src','sport','dst','dport',
'proto','service','duration','bytes'])
# Feature for ML
conn_df['bytes_per_sec'] = (
conn_df['bytes'] / conn_df['duration'].clip(0.001)
)41
במצבי חוסר איזון (class imbalance) — Accuracy חסרת ערך. צריך מדדים מותאמים:
מתוך כל ההתראות, כמה היו אמיתיות? גבוה = פחות False Positives. TP/(TP+FP)
מתוך כל החריגות האמיתיות, כמה גילינו? גבוה = פחות False Negatives. TP/(TP+FN)
ממוצע הרמוני בין Precision ל-Recall. המדד המאוזן ביותר. 2*(P*R)/(P+R)
מודד את יכולת ההפרדה הכוללת של המודל ללא תלות בסף. מומלץ לעקומות.
42
pip install scikit-learn matplotlib
from sklearn.metrics import (
classification_report,
roc_auc_score,
confusion_matrix,
precision_recall_curve
)
import matplotlib.pyplot as plt
# Evaluate model
y_pred = model.predict(X_test)
y_scores = model.decision_function(X_test)
print(classification_report(y_test, y_pred,
target_names=['Normal', 'Anomaly']))
# AUC-ROC
auc = roc_auc_score(y_test, y_scores)
print(f"AUC-ROC: {auc:.4f}")
# Confusion Matrix
cm = confusion_matrix(y_test, y_pred)
print(f"False Positives: {cm[0][1]}")
print(f"False Negatives: {cm[1][0]}")
print(f"True Positives: {cm[1][1]}")from sklearn.metrics import ...ייבוא כלי הערכה: classification_report לדוח מפורט, roc_auc_score לציון AUC, confusion_matrix למטריצת בלבול, precision_recall_curve לעקומת PR
model.predict(X_test)מריצים את המודל על נתוני הבדיקה. מחזיר 1 (נורמלי) או -1 (חריגה) עבור Isolation Forest
model.decision_function(X_test)מחזיר ציון רציף (לא בינארי). ציון נמוך יותר = חריגה חזקה יותר. נדרש לחישוב AUC
classification_report(...)מדפיס Precision, Recall ו-F1 לכל קלאס. הכי חשוב: Recall של קלאס החריגות
roc_auc_score(y_test, y_scores)AUC-ROC: 1.0 = מושלם, 0.5 = אקראי. מדד עיקרי לאיכות מודל זיהוי חריגות
confusion_matrixcm[0][1] = False Positives (התראות שגויות), cm[1][0] = False Negatives (חריגות שפוספסו — הכי מסוכן!)
43

UEBA (User and Entity Behavior Analytics) מרחיבה את זיהוי החריגות מרשת לרמת המשתמש. המטרה: לזהות Insider Threats, גניבת אישורים, ועבירות מדיניות.
כניסות רגילות, שעות עבודה, שרתים נגישים, נפח נתונים
כניסה ממדינה אחרת, גישה לשרת רגיש חדש, הורדת נתונים מסיבית
כל פעולה מקבלת ציון — צבירת ציונים מעל סף מפעילה התראה
44
pip install pandas scikit-learn
import pandas as pd
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import LabelEncoder
# Load user activity logs
logs = pd.read_csv('user_activity.csv')
# Feature engineering per user session
features = logs.groupby(['user_id', 'session_id']).agg({
'hour': 'first',
'country': 'first',
'data_bytes': 'sum',
'resource': 'nunique',
'failed_auth': 'sum'
}).reset_index()
# Encode categorical features
le = LabelEncoder()
features['country_enc'] = le.fit_transform(features['country'])טוענים לוג פעילות משתמשים מקובץ CSV. כל שורה = פעולה אחת של משתמש
מקבצים את הפעולות לפי משתמש וסשן — כל סשן הופך לשורה אחת
שעת תחילת הסשן — חשוב לזיהוי כניסות בשעות חריגות (לילה, סוף שבוע)
סך הבייטים שהועברו בסשן — נפח גבוה חריג עלול להצביע על Exfiltration
מספר משאבים שונים שנגשו אליהם — גישה למשאבים רבים = חשוד
מספר ניסיונות אימות כושלים — אינדיקטור ל-Brute Force
ממיר ערכים קטגוריאליים (כמו שם מדינה) למספרים שהמודל יכול לעבד
45
pip install pandas scikit-learn
# Train per-user baseline (unsupervised)
model = IsolationForest(contamination=0.03)
model.fit(features[numeric_cols])
# Risk score (-1 to 0 range, lower = riskier)
risk_scores = model.decision_function(features[numeric_cols])
features['risk'] = risk_scores
# Flag high-risk sessions
high_risk = features[features['risk'] < -0.1]
print(f"High-risk sessions: {len(high_risk)}")
print(high_risk[['user_id', 'hour', 'data_bytes', 'risk']].sort_values('risk'))contamination=0.03 אומר שאנחנו מצפים ש-3% מהסשנים יהיו חריגים. כוונו לפי הסביבה שלכם
אימון על כל הסשנים ללא תוויות. המודל לומד מה נורמלי לכל משתמש
מחזיר ציון רציף: קרוב ל-0 = נורמלי, שלילי = חריג. שונה מ-predict שמחזיר רק 1/-1
שומרים את ציון הסיכון לכל סשן לצורך דיווח ותעדוף
סף -0.1 מסמן סשנים בסיכון גבוה. ניתן לכוון לפי רמת הרגישות הרצויה
ממיינים מהמסוכן ביותר — מאפשר לאנליסט לטפל בהתראות לפי עדיפות
46

רשתות נוירונים גרפיות (GNNs) הן סוג של מודל למידה עמוקה שתוכנן במיוחד לעבד נתונים המאורגנים במבנה גרפי. הן חיוניות לזיהוי חריגות במערכות מורכבות, שם הקשרים בין הישויות חשובים לא פחות מהישויות עצמן.
בסביבת רשת מחשבים: מחשבים ושרתים הם צמתים (Nodes), והקשרים ביניהם (תקשורת, חיבורים) הם קשתות (Edges).
הוא לוכד את הקשרים והאינטראקציות בין ישויות, לא רק את נקודות הנתונים הבודדות. זה מאפשר הבנה עמוקה יותר של המערכת.
מאפשר לזהות דפוסי התקפה כמו "תנועה רוחבית" (Lateral Movement) שמתפשטת בין מכונות, וקשה לזיהוי בנתוני זרם סטנדרטיים.
כל צומת "לומד" מהשכנים שלו, כלומר, מאגד מידע מהמחשבים או השרתים המחוברים אליו ישירות ולאורך הגרף.
כאשר הקשרים בין ישויות חשובים באותה מידה כמו הישויות עצמן, לדוגמה: רשתות תקשורת, רשתות חברתיות, או מערכות פיננסיות.
47
Graph Anomaly Detection
מייצג את מחשבים, שרתים ומשתמשים
(edges)באמצעות קשתות-nodes ותקשורת ביניהם כ-.
מאפשר זיהוי של דפוסי תקיפה כמו
Lateral Movement
שבלתי אפשרי לזהות בנתוני זרם פשוטים.
pip install torch-geometric
pip install torch numpy
import torch
from torch_geometric.nn import GCNConv
from torch_geometric.data import Data
# Build network graph from connections
edge_index = torch.tensor(
[[src_ids], [dst_ids]], dtype=torch.long)
x = torch.tensor(node_features, dtype=torch.float)
data = Data(x=x, edge_index=edge_index)
# GCN for anomaly scoring
class GraphAnomalyDetector(torch.nn.Module):
def __init__(self):
super().__init__()
self.conv1 = GCNConv(in_channels, 64)
self.conv2 = GCNConv(64, 32)
self.score = torch.nn.Linear(32, 1)ספריית PyTorch Geometric לעבודה עם גרפים.
דורשת התקנה מיוחדת בהתאם לגרסת CUDA.
מטריצת הקשרים:
שורה 0 = מקורות, שורה 1 = יעדים.
כל עמודה = קשר (edge) אחד.
מטריצת פיצ'רים:
כל שורה = node (מחשב/שרת),
כל עמודה = פיצ'ר (כמות חיבורים, bytes וכו').
אובייקט הגרף של PyG.
מכיל את הפיצ'רים והקשרים יחד.
שכבת Graph Convolution: מצבר מידע מהשכנים.
כל node מקבל מידע מהמחשבים שמחובר אליהם.
שכבת פלט:
מייצרת ציון חריגה אחד לכל node ברשת.
47
תעבורת DNS היא אחת הוקטורים הנפוצים ביותר להסתרת מתקפות. C2 Beaconing, DNS Tunneling ו-Data Exfiltration מסתתרים בתוך שאילתות DNS לגיטימיות לכאורה.
import math
def dns_entropy(domain):
freq = {}
for c in domain:
freq[c] = freq.get(c, 0) + 1
total = len(domain)
return -sum(
(v/total)*math.log2(v/total)
for v in freq.values()
)
# High entropy = suspicious
threshold = 3.5
suspicious = df[
df['query'].apply(dns_entropy) > threshold
]48
תוכנות זדוניות מתקשרות לשרת C2
במרווחי זמן קבועים (Beaconing).
זיהוי הדפוס הסדיר הזה בתוך רעש התעבורה
הוא אתגר קלאסי של זיהוי חריגות.
pip install numpy scipy
import numpy as np
from scipy import stats
def detect_beaconing(connection_times, threshold=0.1):
"""Detect periodic communication patterns."""
intervals = np.diff(sorted(connection_times))
if len(intervals) < 5:
return False, 0
# Low coefficient of variation = very regular
cv = intervals.std() / intervals.mean()
# Check periodicity with FFT
fft = np.fft.fft(intervals)
dominant_freq = np.argmax(np.abs(fft[1:])) + 1
is_beacon = cv < threshold
return is_beacon, cv
# Alert if beaconing detected
for host, times in host_connections.items():
is_beacon, score = detect_beaconing(times)
if is_beacon:
print(f"ALERT: {host} beaconing (CV={score:.3f})")intervals = np.diff(sorted(...))ממיינים את זמני החיבור ומחשבים את ההפרשים ביניהם. Beaconing = הפרשים קבועים
if len(intervals) < 5: return False, 0צריך לפחות 5 חיבורים כדי לקבוע דפוס. פחות מזה — אין מספיק נתונים
cv = intervals.std() / intervals.mean()Coefficient of Variation: סטיית תקן חלקי ממוצע. CV נמוך = מרווחים קבועים = Beaconing
fft = np.fft.fft(intervals)Fast Fourier Transform — מזהה תדרים דומיננטיים בנתונים. תדר חזק = תקשורת מחזורית
dominant_freq = np.argmax(np.abs(fft[1:])) + 1מוצאים את התדר הדומיננטי ביותר (מדלגים על DC component)
is_beacon = cv < thresholdאם CV מתחת לסף (ברירת מחדל 0.1) — מסמנים כ-Beaconing חשוד
for host, times in host_connections.items()עוברים על כל מארח ברשת ובודקים את דפוס החיבורים שלו
49 / 68
מודל שעובד בפיתוח אינו מובטח לעבוד בייצור.
שינויי תעבורה, מתקפות חדשות ו-Data Drift
דורשים ניטור ואימון מחדש מתמשך.
50
Data Drift מתרחש כשדפוסי התעבורה משתנים לאורך זמן: עדכוני אפליקציות, מעבר לענן, עבודה מהבית. מודל שאומן על תעבורה ישנה יתחיל להפיק False Positives מסיביים.
from alibi_detect.cd import (
KSDrift, # Kolmogorov-Smirnov
MMDDrift # Maximum Mean Discrepancy
)
# Monitor for distribution changes
drift_detector = KSDrift(
x_ref=X_reference,
p_val=0.05
)
result = drift_detector.predict(X_new)
if result['data']['is_drift']:
print("DRIFT DETECTED — retrain needed")
trigger_retraining_pipeline()from alibi_detect.cd import KSDrift, MMDDriftייבוא גלאי Drift: KSDrift = Kolmogorov-Smirnov (לפיצ'רים בודדים), MMDDrift = Maximum Mean Discrepancy (לכל הפיצ'רים יחד)
KSDrift(x_ref=X_reference, p_val=0.05)יצירת גלאי. x_ref = נתוני הייחוס (תעבורה נורמלית ישנה). p_val=0.05 = רמת מובהקות סטטיסטית
drift_detector.predict(X_new)בדיקה האם הנתונים החדשים שונים סטטיסטית מנתוני הייחוס
result['data']['is_drift']True אם זוהה Drift.== False אם הנתונים עדיין דומים לייחוס
trigger_retraining_pipeline()הפעלת פייפליין אימון מחדש אוטומטי. בייצור: מחובר ל-MLflow, Airflow או Kubeflow
51

SHAP מסביר
למה מודל סיווג רשומה כחריגה.
קריטי לאנליסטי מרכז האבטחה
שצריכים להבין ולאמת כל התראה.
pip install shap
import shap
# Explain Isolation Forest decisions
explainer = shap.TreeExplainer(isolation_forest_model)
shap_values = explainer.shap_values(X_test)
# Plot feature importance for anomaly
shap.waterfall_plot(
shap.Explanation(
values=shap_values[anomaly_idx],
base_values=explainer.expected_value,
data=X_test[anomaly_idx],
feature_names=feature_names
)
)
# Summary plot - which features matter most?
shap.summary_plot(shap_values, X_test,
feature_names=feature_names)52
SIEM (Security Information and Event Management)
הוא מערכת מרכזית שאוספת לוגים והתראות
מכל רכיבי הרשת — שרתים, פיירוולים, אנטי-וירוס ועוד
ומאפשרת לאנליסט לראות את כל התמונה במקום אחד.
הצעד הקריטי האחרון הוא חיבור המודל ל-SIEM הארגוני.
נתמקד ב-Elastic SIEM
כדוגמה לקוד פתוח.
pip install elasticsearch
from elasticsearch import Elasticsearch
from datetime import datetime
import json
es = Elasticsearch(['http://localhost:9200'])
def send_alert(anomaly_data, score, explanation):
"""Send anomaly alert to Elasticsearch/SIEM."""
alert = {
"@timestamp": datetime.utcnow().isoformat(),
"event.category": "network",
"event.type": "anomaly",
"anomaly.score": float(score),
"anomaly.explanation": explanation,
"source.ip": anomaly_data['src_ip'],
"destination.ip": anomaly_data['dst_ip'],
"network.bytes": anomaly_data['bytes'],
"severity": "high" if score > 0.8 else "medium"
}
es.index(
index=f"anomalies-{datetime.now():%Y.%m.%d}",
document=alert
)
print(f"Alert sent: {anomaly_data['src_ip']}")es = Elasticsearch([...])יצירת חיבור ל-Elasticsearch. בייצור: החליפו localhost בכתובת ה-SIEM האמיתית עם אימות
@timestamp: datetime.utcnow()חותמת זמן UTC — קריטי לקורלציה עם אירועים אחרים ב-SIEM
event.category / event.typeשדות ECS (Elastic Common Schema) — תקן אחיד שמאפשר חיפוש אחיד בין מקורות שונים
anomaly.score: float(score)ציון החריגה (0-1). מאפשר לאנליסט לתעדף התראות לפי חומרה
anomaly.explanationהסבר מה גרם לחריגה (מ-SHAP). הופך את ההתראה לניתנת לפעולה
severity: high if score > 0.8סיווג חומרה אוטומטי. ניתן להוסיף low לציונים מתחת ל-0.5
es.index(index=f'anomalies-...')שמירה לאינדקס יומי. מאפשר ניהול retention policy ומחיקה אוטומטית של נתונים ישנים
53

pip install tensorflow numpy
import tensorflow as tf
class VAE(tf.keras.Model):
def __init__(self, latent_dim):
super().__init__()
self.latent_dim = latent_dim
self.encoder = self._build_encoder()
self.decoder = self._build_decoder()
def call(self, x):
z_mean, z_log_var = self.encoder(x)
# Reparameterization trick
epsilon = tf.random.normal(
shape=tf.shape(z_mean))
z = z_mean + tf.exp(0.5*z_log_var)*epsilon
reconstructed = self.decoder(z)
# KL Divergence loss (regularization)
kl_loss = -0.5 * tf.reduce_mean(
1 + z_log_var - tf.square(z_mean)
- tf.exp(z_log_var))
self.add_loss(kl_loss)
return reconstructedclass VAE(tf.keras.Model)הגדרת VAE כמחלקת Keras מותאמת. יורשת מ-tf.keras.Model כדי לאפשר לוגיקת אימון מותאמת
self.encoder = self._build_encoder()הEncoder דוחס את הקלט לפרמטרים של התפלגות (z_mean ו-z_log_var) — לא לנקודה אחת
z_mean, z_log_var = self.encoder(x)הEncoder מחזיר שני וקטורים: ממוצע ולוג-שונות של ההתפלגות הנורמלית הנלמדת
epsilon = tf.random.normal(...)דגימה אקראית מהתפלגות נורמלית. זה מה שהופך את ה-VAE לגנרטיבי
z = z_mean + tf.exp(0.5*z_log_var)*epsilonReparameterization Trick: מאפשר גרדיאנטים לזרום דרך הדגימה האקראית
kl_loss = -0.5 * tf.reduce_mean(...)KL Divergence: מדד כמה ההתפלגות הנלמדת קרובה לנורמלית סטנדרטית. מונע overfitting
self.add_loss(kl_loss)הוספת KL loss לפונקציית ההפסד הכוללת. ה-VAE ממזג שגיאת שחזור + KL divergence
54
LSTM ו-VAE ֿ
מובילים בביצועים אך דורשים יותר משאבי חישוב.
Isolation Forest
מציע את האיזון הטוב ביותר בין ביצועים למהירות.
55
הגרף מראה בבירור: Isolation Forest הוא המהיר ביותר (2ms לחיזוי), בעוד LSTM ו-VAE מציעים דיוק גבוה עם עיכוב מינימלי. One-Class SVM הוא האיטי ביותר עם הדיוק הנמוך ביותר.
56

ב-Semi-Supervised Learning משתמשים בכמות קטנה של דוגמאות מתויגות (ידנית ע"י אנליסטים) לצד כמות גדולה של נתונים לא מתויגים. מחקרים מראים שיפור של 15-25% ב-F1 לעומת שיטות לא מפוקחות לחלוטין.
from sklearn.semi_supervised import (
LabelPropagation,
LabelSpreading
)
import numpy as np
# -1 = unlabeled, 0 = normal, 1 = anomaly
y_mixed = np.full(len(X), -1) # all unlabeled
y_mixed[labeled_normal_idx] = 0
y_mixed[labeled_anomaly_idx] = 1
# Label Propagation spreads known labels
model = LabelSpreading(
kernel='rbf',
alpha=0.2,
max_iter=100
)
model.fit(X, y_mixed)
predictions = model.predict(X_test)57
Active Learning מבקש מהאנליסט לתייג רק את הדוגמאות המסוכנות ביותר — אלה שהמודל הכי לא בטוח בהן. כך ניתן לשפר את המודל עם מינימום מאמץ אנושי.
from modAL.models import ActiveLearner
from modAL.uncertainty import uncertainty_sampling
learner = ActiveLearner(
estimator=IsolationForest(contamination=0.05),
query_strategy=uncertainty_sampling,
X_training=X_initial,
y_training=y_initial
)
# Query the most informative samples
query_idx, query_sample = learner.query(X_pool, n_instances=10)
# Analyst labels these 10 samples
y_new = analyst_review(query_sample)
learner.teach(X_pool[query_idx], y_new)58
,Suricata הוא כלי ניטור רשת בקוד פתוח מסוג IDS/IPS
.כלומר מערכת שמזהה ומונעת חדירות.
שילובו עם מודל למידת מכונה ,מאפשר להעשיר את ההתראות המסורתיות
,עם ציוני חריגות מבוססי בינה מלאכותית,
.ובכך להפחית התראות שגויות
pip install pyod
import json
import subprocess
from pyod.models.iforest import IForest
def process_suricata_alert(alert_json):
"""Enrich Suricata alert with ML anomaly score."""
alert = json.loads(alert_json)
features = extract_flow_features(alert)
score = ml_model.decision_function([features])[0]
# Enrich alert with ML score
alert['ml_anomaly_score'] = float(score)
alert['ml_is_anomaly'] = score < threshold
alert['ml_confidence'] = abs(score)
return alert
# Tail Suricata EVE JSON log
with open('/var/log/suricata/eve.json', 'r') as f:
for line in follow(f):
enriched = process_suricata_alert(line)
if enriched['ml_is_anomaly']:
send_high_priority_alert(enriched)import json, subprocessjson לפענוח התראות Suricata בפורמט EVE JSON. subprocess לתקשורת עם תהליך Suricata
def process_suricata_alert(alert_json)פונקציה שמקבלת התראת Suricata גולמית ומעשירה אותה עם ציון ML
extract_flow_features(alert)חילוץ פיצ'רים מהתראה: IP מקור/יעד, פורטים, bytes, משך חיבור וכו'
decision_function([features])[0]הרצת המודל על הפיצ'רים. ציון נמוך = חריגה חזקה יותר
alert['ml_anomaly_score']הוספת ציון ML להתראה המקורית. ההתראה המועשרת נשלחת ל-SIEM
score < thresholdסיווג בינארי: האם ההתראה היא חריגה אמיתית לפי המודל
for line in follow(f)קריאה רציפה מקובץ הלוג של Suricata. follow() = tail -f בפייתון — ממתין לשורות חדשות
59
# Dockerfile for anomaly detection service
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["uvicorn", "main:app",
"--host", "0.0.0.0",
"--port", "8080"]
# docker-compose.yml
version: '3.8'
services:
anomaly-detector:
build: .
ports: ["8080:8080"]
volumes:
- ./models:/app/models
environment:
- MODEL_PATH=/app/models/isolation_forest.pkl
- THRESHOLD=0.05
- KAFKA_BROKER=kafka:9092
kafka:
image: confluentinc/cp-kafka:latest
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
elasticsearch:
image: elasticsearch:8.8.0FROM python:3.11-slimתמונת בסיס Python רזה. slim = ללא חבילות מיותרות, מקטין את גודל ה-image
WORKDIR /appהגדרת תיקיית עבודה בתוך הקונטיינר. כל הפקודות הבאות יורצו מכאן
COPY requirements.txt . / RUN pip installהעתקת קובץ הדרישות והתקנת ספריות. שלב נפרד מ-COPY . . כדי לנצל Docker cache
EXPOSE 8080הצהרה שהקונטיינר מאזין על פורט 8080. לא פותח את הפורט בפועל — זה נעשה ב-docker run
CMD [uvicorn, main:app, --host 0.0.0.0, --port 8080]פקודת ההפעלה. 0.0.0.0 = מאזין על כל ממשקי הרשת (חובה בקונטיינר)
volumes: - ./models:/app/modelsMount של תיקיית המודלים. מאפשר עדכון מודל ללא בנייה מחדש של ה-image
environment: MODEL_PATH, THRESHOLD, KAFKA_BROKERמשתני סביבה לקונפיגורציה. מאפשרים לשנות הגדרות ללא שינוי קוד
60
pip install fastapi uvicorn pydantic scikit-learn numpy
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import pickle
import numpy as np
app = FastAPI(title="Network Anomaly Detection API")
# Load pre-trained model
with open('model.pkl', 'rb') as f:
model = pickle.load(f)
class NetworkFlow(BaseModel):
src_bytes: float
dst_bytes: float
duration: float
protocol: int
src_port: int
dst_port: int
class AnomalyResult(BaseModel):
is_anomaly: bool
score: float
severity: str
@app.post("/predict", response_model=AnomalyResult)
async def predict_anomaly(flow: NetworkFlow):
features = np.array([[
flow.src_bytes, flow.dst_bytes,
flow.duration, flow.protocol,
flow.src_port, flow.dst_port
]])
score = model.decision_function(features)[0]
is_anomaly = score < -0.1
severity = "high" if score < -0.5 else "medium"
return AnomalyResult(is_anomaly=is_anomaly,
score=float(score), severity=severity)app = FastAPI(title='Network Anomaly Detection API')יצירת אפליקציית FastAPI. title מופיע בדוקומנטציה האוטומטית ב-/docs
with open('model.pkl', 'rb') as f: model = pickle.load(f)טעינת המודל המאומן מקובץ. pickle = פורמט סריאליזציה של Python. נטען פעם אחת בהפעלה
class NetworkFlow(BaseModel)הגדרת סכמת הקלט עם Pydantic. FastAPI מאמת אוטומטית שכל שדה קיים ומהסוג הנכון
class AnomalyResult(BaseModel)הגדרת סכמת הפלט. FastAPI מייצר תיעוד API אוטומטי מהמחלקות האלו
@app.post('/predict', response_model=AnomalyResult)הגדרת endpoint POST. response_model מאמת שהפלט תואם לסכמה
features = np.array([[flow.src_bytes, ...]])המרת אובייקט Pydantic למערך NumPy שהמודל יכול לעבד
score = model.decision_function(features)[0]הרצת המודל. [0] כי decision_function מחזיר מערך — לוקחים את הערך הראשון
severity = 'high' if score < -0.5 else 'medium'סיווג חומרה אוטומטי לפי ציון. ניתן להוסיף low לציונים קרובים ל-0
61

בחברה פיננסית מובילה — זוהתה מתקפת DDoS מסוג SYN Flood תוך 8 שניות ממתחילתה, לפני שהשרות הושפע. הנה הגישה:
עלייה חריגה בחיבורי SYN חלקיים — מודל מסמן חריגה
River מעדכן מודל Online ומאשר דפוס DDoS
התראה נשלחת ל-SIEM + חסימה אוטומטית ב-firewall
SOC מקבל דוח מלא עם SHAP explanation
62
עובד בכיר ניסה להוציא מאגר לקוחות שבועיים לפני עזיבתו. מערכת UEBA זיהתה את הדפוס:
העובד ניגש לשרת שמעולם לא ניגש אליו — ציון חריגות: 0.72
300MB ב-23:00 — חריגה כפולה: שעה ונפח. ציון: 0.89
חיבור התקן נייד — ציון מצטבר עבר 0.95 — התראה קריטית
SOC חסם את החשבון לפני ביצוע ההעברה הסופית
63

ניטור התנהגות משתמשים מעלה שאלות משפטיות ואתיות קריטיות. מערכות UEBA חייבות להיות מעוצבות עם Privacy by Design.
GDPR, חוק הגנת הפרטיות הישראלי — אנונימיזציה של נתונים אישיים
עובדים צריכים להיות מודעים לניטור — הסכמה מפורשת
בדיקת fairness — המודל לא יפלה על בסיס מאפיינים לא רלוונטיים
64
Federated Learning מאפשר לאמן מודל זיהוי חריגות משותף בין ארגונים מרובים — ללא שיתוף נתוני רשת רגישים. כל ארגון מאמן מקומית ומשתף רק את עדכוני המשקולות.
pip install flwr # Flower - Federated Learning
import flwr as fl
import numpy as np
class AnomalyClient(fl.client.NumPyClient):
def get_parameters(self, config):
return model.get_weights()
def fit(self, parameters, config):
model.set_weights(parameters)
model.fit(local_X_train, local_X_train,
epochs=1, batch_size=256)
return model.get_weights(), len(local_X_train), {}
def evaluate(self, parameters, config):
model.set_weights(parameters)
loss = model.evaluate(local_X_test, local_X_test)
return loss, len(local_X_test), {}
fl.client.start_numpy_client(
server_address="federated-server:8080",
client=AnomalyClient()
)pip install flwrהתקנת Flower — ספריית Federated Learning הפופולרית ביותר ב-Python. תומכת ב-TensorFlow, PyTorch ו-scikit-learn
class AnomalyClient(fl.client.NumPyClient)הגדרת לקוח Federated. כל ארגון מריץ instance של המחלקה הזו על הנתונים המקומיים שלו
def get_parameters(...): return model.get_weights()שליחת משקולות המודל המקומי לשרת המרכזי. הנתונים עצמם לא נשלחים — רק המשקולות
def fit(self, parameters, config)קבלת משקולות מהשרת, אימון מקומי על הנתונים הפרטיים, החזרת משקולות מעודכנות
model.set_weights(parameters)עדכון המודל המקומי עם המשקולות המצורפות מהשרת (ממוצע של כל הארגונים)
model.fit(local_X_train, ..., epochs=1)אימון epoch אחד על הנתונים המקומיים. הנתונים לא יוצאים מהארגון
fl.client.start_numpy_client(server_address=...)חיבור לשרת הפדרטיבי המרכזי. השרת מתאם בין כל הלקוחות ומחשב ממוצע משקולות
65
# 1. Create virtual environment
python -m venv anomaly_env
source anomaly_env/bin/activate # Linux/Mac
# anomaly_env\Scripts\activate # Windows
# 2. Install all required packages
pip install scikit-learn pyod alibi-detect \
tensorflow torch torch-geometric \
river shap mlflow elasticsearch \
fastapi uvicorn scapy pandas \
matplotlib seaborn jupyter
# 3. Download CICIDS2017 dataset
# from: https://www.unb.ca/cic/datasets/ids-2017.html
# 4. Start Jupyter notebook
jupyter notebook anomaly_detection.ipynb
# 5. Start MLflow tracking server
mlflow server --host 0.0.0.0 --port 5000
# 6. Run anomaly detection service
uvicorn main:app --reload --port 8080print("Checking installations...")
try: import numpy; print("numpy OK")
except: print("numpy MISSING")
try: import sklearn; print("scikit-learn OK")
except: print("scikit-learn MISSING")
try: import pyod; print("pyod OK")
except: print("pyod MISSING")
try: import alibi_detect; print("alibi-detect OK")
except: print("alibi-detect MISSING")
try: import river; print("river OK")
except: print("river MISSING")
try: import tensorflow; print("tensorflow OK")
except: print("tensorflow MISSING")
try: import torch; print("torch OK")
except: print("torch MISSING")
try: import shap; print("shap OK")
except: print("shap MISSING")
print("Done!")docker pull jupyter/scipy-notebook
docker run -p 8888:8888 -v $(pwd):/home/jovyan/work jupyter/scipy-notebook
# Then inside the container:
pip install pyod alibi-detect river shap torch-geometricהשתמשו בגרסה המתאימה ל-CUDA שלכם:
pip install torch-geometric -f https://data.pyg.org/whl/torch-2.0.0+cpu.html
ללא GPU: pip install tensorflow-cpu
עם GPU: pip install tensorflow[and-cuda]
צרו venv נפרד לאליבי:
python -m venv alibi_env
66
67

כעת תיישמו את מה שלמדתם. בחרו אחד מהתרגילים הבאים בהתאם לרמתכם:
טענו את CICIDS2017, חלצו 5 פיצ'רים, אמנו Isolation Forest והדפיסו Confusion Matrix.
בנו Autoencoder ב-Keras, אמנו על תעבורה נורמלית בלבד, הציבו סף Reconstruction Error.
חברו River ל-Kafka Stream, הוסיפו SHAP explanation, שלחו התראות ל-Elasticsearch.
68
מה ההבדל בין חריגה נקודתית לקונטקסטואלית? תנו דוגמה מתחום הרשתות.
מתי תעדיפו LSTM על Isolation Forest לזיהוי חריגות? הסבירו את הסיבות.
כיצד SHAP עוזר לאנליסט SOC? תארו תרחיש שימוש ספציפי.
מה Data Drift ואיך Alibi Detect מסייע בזיהויו בייצור?
הסבירו את עיקרון ה-Reconstruction Error ב-Autoencoder ואיך קובעים את הסף.
מדוע Federated Learning מתאים לשיתוף פעולה בין ארגונים בתחום הסייבר?
69

השלמנו מסע מקיף מתיאוריה לייצור — יש לכם כעת את הכלים לבנות מערכת זיהוי חריגות ברמת enterprise.
הצעד הבא: במפגש 3 נעלה רמה — LLMs לניתוח לוגים ו-SOC Automation עם LangChain.
70
זיהוי חריגות ברשת עם ML