Created by Petteri Nevavuori <petteri.nevavuori@gmail.com>
CHIO & FREEMAN: MACHINE LEARNING & SECURITY (2018)
Otsikot kirjan mukaan, muutoin suomeksi.
Internetin yleistymisen myötä myös sen käyttö asiattomasti on yleistynyt suuresti. Keskimääräiselle käyttäjälle näkyvin muoto ovat roskasähköpostit (spam). Tämän lisäksi kyberturvallisuudessa keskitytään mm. seuraaviin osa-alueisiin: tunkeutuminen (intrusion), web-pohjaisten ohjelmien turvallisuus (web application security), haittaohjelmat (malware) ja sosiaalisten verkkojen turvallisuus (social network security).
Koska roskaposti on haitoista yleisin, kiinnitettiin sen tunnistamiseen huomiota myös koneoppimismenetelmien kehittyessä. Roskapostin suhteen ollaankin jo tilanteessa, jossa lähes 100% saapuvasta roskapostista suodatetaan onnistuneesti. Keskiössä on data, jota käyttämällä voitiin alkaa rakentaa erilaisiin kyberturvallisuuden tehtäviin sopivia malleja uhkien tai anomalioiden tunnistamiseksi.
Alati teknologistuvassa ja verkkoon kytkeytyvämmässä yhteiskunnassa kyberturvallisuusuhkien kenttä laajenee, minkä vuoksi datapohjaisten mallien käytölle turvallisuuden takaamiseksi on kysettä. IT-järjestelmien ja verkkopalvelun keskittyneisyys (centralization) asettaa aina suuremmat käyttäjämäärät turvallisuusuhan alle. Perusasetelma pysyy kuitenkin samana - hyökkääjä pyrkii aina hyväksikäyttämään kohdejärjestelmiensä heikkouksia ja puolustaja vuorostaan korjaamaan ja vahvistamaan niitä.
Tässä kirjassa käydään läpi koneoppimisen ja data-analyysin eri menetelmien soveltamista kyberturvallisuuden osa-alueilla. Ensimmäisessä luvussa rakennetaan viitekehys loppukirjan aiheille keskustelemalla tietokoneiden ja verkkojen uhista, koneoppimisen luonteesta ja soveltuvuudesta kyberturvallisuuteen.
Yleisimmät kyberturvallisuusuhat jaotellaan kirjassa seuraavasti ylätasolla tiedonkeruuseen (information gathering), tunkeutumiseen, tunkeutumisyrityksiin, petokseen (fraud), hyväksikäyttävään sisältöön (abusive content), haittaohjelmiin (malware) ja saatavuushyökkäyksiin (availability attacks). Tarkemmin näitä ylätasoja laajennetaan alla olevan kuvan mukaisesti. Ylätason katkoviivanuolet ilmaisevat osa-alueiden keskinäisiä vuorovaikutussuhteita.
Verkkorikollisuus on ajan kuluessa muuttunut yksittäisen toimijan maineen kasvattamisesta mittavaksi liiketoiminnaksi - motivaatio hyökkäykseen on usein raha. Raha saa sekä tulon toiveissa olevat toimijat kiinnostumaan hyökkäyksistä että tulojaan suojaavat toimijat vahvistamaan suojauksiaan. Korkeamman suojaustason vuoksi myös rahallisesti vähemmän arvokkaat kohteet ovat kiinnostavia. Liiketoiminnalistumisen eräs lieveilmiö on myös hyökkääjän hakkerointitaitojen vaatimustason alentuminen. Internetissä on markkinapaikkoja, joiden kautta on mahdollista ostaa sopivia ohjelmia haluttujen hyökkäysten toteuttamiseksi.
Suoran markkinamaisen rahoituksen lisäksi hyökkääjät voivat saada tuloja epäsuorasti. Eräs tapa on hyökkäykseen soveltuvien IT-infrastruktuurien kapasiteetin tarjonta. Bottiverkot (botnet) liittyvät vahvasti näihin, ja niiden käytöstä maksetaan hyvin samaan tapaan, kuin perinteisistä pilvilaskentapalveluista. Vakoiluohjelmilla taas saadaan kerättyä tietoa, jota sittemmin voidaan myydä tulojen toivossa. Joka tapauksessa motivaattorina on nykyisin raha (kyseessä huijauksia paljastava striimaaja).
Koneoppimisella (machine learning) tarkoitetaan algoritmeja ja prosesseja, joilla voidaan esimerkiksi muodostaa ennusteita tulevasta käyttämällä menneestä kerättyä dataa. Ytimessään kyse on matematiikan ja optimoinnin tekniikoista yhdistettynä koneelliseen laskentaan datasta louhitun informaation, kuvaavien piirteiden (feature) ja päätelmien (inference) tuottamiseksi.
Ohjatut (supervised) menetelmät muodostavat todennäköisyysmalleja käyttäen tietoja menneistä tapahtumista ja niiden seurauksista. Ohjaamattomat (unsupervised) menetelmät taas muodostavat yleisiä kuvauksia luokittelemattomasta datasta ositellen sitä. Yleisimmät koneoppimistehtävät (machine learning task) ovat joko luokittelua (classification) tai ennustusta (regression). Ohjattua regressiota on lämpötilan ennustaminen käyttäen syötteenä (input) monia eri sääolosuhteita kuvaavia muuttujia ja kohteena (target) kulloinkin mitatun sääolosuhteen lämpötilaa. Ohjaamatonta luokittelua on kaupan asiakkaiden erottelu heistä kerättyjen tietojen perusteella siten, että erottelu tehdään ryhmittelemällä samankaltaiset asiakkaat omiksi ryhmikseen.
Yksi merkittävimmistä konsepteista koneoppimismenetelmissä on piirre. Menetelmät pyrkivät niille syötetyistä datanäytteistä (sample) sellaisia yleisluontoisia ominaisuuksia, jotka sopivat eri tavoin painotettuina lähes kaikkiin näytteisiin. Tavoitteena on, että yksittäisiä näytteitä käsittelemällä menetelmä oppisi edes jossain määrin merkittävästi mallintamaan sitä prosessia, joka on tuottanut menetelmälle syötettyjä näytteitä. Menetelmät pyrkivät mallintamaan tätä prosessia oppimalla kuvaavia piirteitä (descriptive features).
Tekoäly (artificial intelligence) on yleinen ja löyhästi määritelty tyypillisesti ihmisten kohtaamien ongelmien algoritmista ratkaisua tarkoittava kattotermi. Tekoäly on kokonaisuus, missä koneoppiminen on vain yhtenä, joskin kiinteänä osa-alueena. Syväoppiminen (deep learning) yhdistetään myös usein tekoälyyn, mutta se on itseasiassa vain yksi koneoppimisen osa-alueista. Alla oleva kuva havainnollistaa näiden kolmen suhdetta.
Kyberturvallisuuden näkökulmasta sekä puolustajat että hyökkääjät voivat molemmat hyödyntää koneoppimista oppimalla kumpikin toisistaan käyttämällä vastapuolestaan kerättyä dataa ja mallintamalla sitä. Myös itse koneoppimisen menetelmiä voidaan murtaa, mikä asetta myös mallintamiselle turvallisuusvaatimuksia kyberturvallisuuden osa-alueella.
Kirjassa käsitellään laaja-alaisesti niitä tietoturvallisuuden osa-alueita, joissa koneoppimisella on saavutettu selkeitä etuja. Luvuissa on paljon esimerkkitehtäviä, joilla valotetaan myös koneoppimisen käyttöä kyberturvallisuuden ongelmien ratkaisua käytännössä. Käytetyt koneoppimismenetelmät ei ole itsessään uusia, mutta menetelmien yhdistäminen käsillä olevaan kontekstiin tekee aiheesta mielekkään käsitellä.
Turvallisuuden kanssa käyttökelpoiset koneoppimisen menetelmät voidaan jakaa karkeasti kahteen pääkategoriaan: dataa kuvaavien piirteiden tunnistukseen (pattern recognition) ja poikkeamien havaitsemiseen (anomaly detection). Näiden kahden välinen raja ei ole aivan selkeä, mutta menetelmät lähestyvät ongelmien ratkaisemista eri suunnista. Piirteiden tunnistuksessa pyritään tunnistamaan ennalta tuntemattomista tapahtumista samankaltaisuuksia. Poikkeamien havaitsemissa pyritään taas erottelemaan normaalista toiminnasta poikkeavat havainnot, joita voi olla hyvinkin kirjavasti sekä sisällön että määrän osalta.
Eroa ja toisaalta samankaltaisuutta voidaan havainnollistaa esimerkillä. Piirteiden tunnistuksessa roskapostin tunnistus on arkkityyppiesimerkki. Tällöin malli opetetaan tunnistamaan roskaposteille yleisiä piirteitä ja sovittamaan niitä uusiin, mallin ennen havaitsemattomiin roskaposteihin. Mikäli opitut piirteet sopivat syötettyyn näytteeseen hyvin, luokitellaan se roskapostiksi. Toisaalta roskapostin tunnistusta voidaan lähestyä myös poikkeamien havaitsemisen kautta oppimalla oikeiden sähköpostien kuvaavimmat piirteet. Mikäli malli saa nämä opittuaan roskapostin käsiteltäväkseen, se ei kykene sovittamaan oppimiaan oikeiden postien piirteitä saamaansa näytteeseen ja hylkää postin merkiten sen roskapostiksi.
Muita vastaavia käyttökohteita ovat mm. haittaohjelmien ja bottiverkkojen havaitseminen, autentikonti- ja käyttäytymisanalyysi sekä sovellusten tietoturva-aukkojen löytäminen ja hyväksikäyttö, joista viimeiseen liittyy termi sumeutus (fuzzing, vrt fuzzy logic eli sumea logiikka). Sumeutuksessa sovellus pyritään saamaan toimimaan vastoin alkuperäistä suunnittelua pommittamalla sitä syötteillä ja kutsuilla, jotka esimerkiksi osoittautuneet tehokkaiksi murtokeinoiksi aiemmin. Koneoppimista voidaan hyödyntää tässä todennäköisimpien heikkouksien mallintamisessa.
Roskapostisuodatus on jo aiemmin todettu yhdeksi tietoturvan osa-alueeksi, johon koneoppimista on sovellettu menestyksekkäästi. Tässä osiossa rakennetaankin roskapostisuodatin edeten iteratiivisesti yksinkertaisesti monimutkaisempaan. Mallien koulutukseen käytetään valmiiksi luokiteltuja sähköposteja sisältävää 20017 TREC Public Spam Corpus datasettiä. Tämän lisäksi käytämme hyödyksi kirjan GitHub-projektista löytyviä aputoimintoja, joskin muokattuna omaan tarpeeseen.
Tässä vaiheessa tärkeää ei ole ymmärtää menetelmien toimintatapoja, vaan käydä vain läpi eräitä käytännön koneoppimisen sovelluksia tietoturvaongelmaan.
Mustalistauksen (blacklisting) ideana on, että sieltä löytyvät sanat esiintyvät vain roskaposteissa. Ensimmäisessä iteraatiossa rakennetaankin siis malli, joka tutkii sähköpostien sanat läpi ja luokittelee sähköpostit siten oikeaksi sähköpostiksi tai roskapostiksi. Keskiössä on tekstin käsittely siten, että sanoja käsitellään nltk
-paketilla (Natural Language ToolKit) perusmuotoisiksi. Tämän jälkeen muodostetaan sanastot kummallekin sähköpostiluokalle, minkä jälkeen muodostetaan vain roskaposteissa esiintyvien sanojen mustalista.
Sähköpostidatasetin apufuntiomoduuli löytyy tiedostosta utils/email_utils.py
. Se kannattaa käydä läpi, jotta tietää, mitä konepellin alla tapahtuu.
Ensin siis ladataan datasetti käyttöön määritetyistä sijainneista, minkä jälkeen siitä erotellaan koulutus- ja testisetit. Settien erottelun jälkeen muodostetaan koulutussetin pohjalta mustalista.
import os
import pandas as pd
from utils import email_utils
from IPython.display import display
ROOT_DIR = os.path.join(os.path.dirname(os.getcwd()), 'datasets', 'trec07p')
DATA_DIR = os.path.join(ROOT_DIR, 'data')
LABELS_FILE = os.path.join(ROOT_DIR, 'full', 'index')
TRAINING_SET_RATIO = 0.7
labels = email_utils.read_labels(LABELS_FILE)
data = os.listdir(DATA_DIR)
x_train = data[int(len(data)*TRAINING_SET_RATIO):]
x_test = data[:int(len(data)*TRAINING_SET_RATIO)]
ham_words, spam_words = set(), set()
for filename in x_train:
path = os.path.join(DATA_DIR, filename)
if filename in labels:
label, stems = labels[filename], email_utils.load_stems(path)
if not stems:
continue
ham_words.update(stems) if label == 1 else spam_words.update(stems)
blacklist = spam_words - ham_words
print("Joitain mustalistattuja sanoja:")
print(", ".join(list(blacklist)[:10]))
print(f"Malli löysi kaikenkaikkiaan {len(blacklist)} mustalistattua sanaa.")
Joitain mustalistattuja sanoja: fattygo, ichzb3vzzg9tywluzskgicagicagicagica8l3nwyw4+icagicagicagicagpgxpignsyxnz, dvt.nnoemd.com, 8130, o^gp, ootbal, h4-ptb-4-5lk-678d, pmyahoo, contrarious.sdf39.com/a/d/814/1038582, 199.95 Malli löysi kaikenkaikkiaan 55564 mustalistattua sanaa.
Tämän jälkeen luokitellaan testisetin sähköpostit. Luokittelusta muodostetaan matriisi, jossa on omissa sarakkeissaan oikein (O) ja väärin (V) luokitellut sekä positiiviset (P) että negatiiviset (N) luokittelut. Positiivinen luokittelu tarkoittaa oikeaa sähköpostia, negatiivinen roskapostia. Alla oleva kuva havainnollistaa tätä.
Huomionarvoista on, että kirjan implementaatio laskee tulokset väärin. Sen mukaan mallin ennustaessa roskapostia (0) ja todellisuuden ollessa oikea posti (1) on kyseessä väärä positiivinen, vaikka kyseessä on väärä negativiinen mallin näkökulmasta.
Tulokset esitetään sekaannusmatriisin (confusion matrix) muodossa. Matriisin sisältö on seuraavanlainen:
Tulokset | Tosi: 0 | Tosi: 1 | |
---|---|---|---|
Malli: 0 | ON | VP | |
Malli: 1 | VN | OP |
true_pos, true_neg, false_pos, false_neg = 0, 0, 0, 0
for filename in x_test:
path = os.path.join(DATA_DIR, filename)
if filename in labels:
label, stems = labels[filename], email_utils.load_stems(path)
if not stems:
continue
if stems & blacklist: # Malli: Spam
if label == 1:
false_neg += 1
else:
true_neg += 1
else: # Malli: Mail
if label == 1:
true_pos += 1
else:
false_pos += 1
results = pd.DataFrame(data=[[true_neg, false_pos], [false_neg, true_pos]],
index=['model_0', 'model_1'], columns=['true_0', 'true_1'])
results_p = results/results.sum().sum()*100
print("Tulokset määrissä:")
display(results)
print("Tulokset prosenteissa:")
display(results_p)
print(
f'Mallin tarkkuus (ON+OP): {results_p.iloc[0,0]+results_p.iloc[1,1]:.3f}%')
Tulokset määrissä:
true_0 | true_1 | |
---|---|---|
model_0 | 18189 | 12985 |
model_1 | 2755 | 14769 |
Tulokset prosenteissa:
true_0 | true_1 | |
---|---|---|
model_0 | 37.350610 | 26.664339 |
model_1 | 5.657317 | 30.327734 |
Mallin tarkkuus (ON+OP): 67.678%
Huomionarvoista on, että malli löytää paljon paremmin roskapostit kuin oikeat postit. Oikeista posteista lähes puolet (~13K) luokitellaan roskaposteiksi, kun taas roskaposteista vain alle kolmisen tuhatta luokitellaan väärin.
Mustalistausta hieman kehittyneempi algoritmi on yhteistyösuodatus (collaborative filtering), jonka perusajatuksen mukaan samankaltaiset sähköpostit muodostavat tietyllä tapaa käsiteltynä samankaltaisia tiivisteitä (hash). Samaan perusajatukseen kuuluu myös oletus, että roskapostien lähettäjät muodostavat roskaposteja jonkin samankaltaisia posteja tuottavan automaation avulla. Implementaatiossa hyödynnetään MinHash-algoritmin Python implementaatiota, jossa sähköpostien sanasisällöistä muodostetaan keskenään vertailtavia tiivisteitä ja sen jälkeen lasketaan tiivisteiden samankaltaisuus eli Jaccardin samankaltaisuusindeksi (Jaccard similarity index).
Tässä toteutuksessa hyödynnetään edellisiä datoja. Siksi niitä ei ole tarpeen alustaa uudelleen, mikäli notebookin solut ajetaan järjestyksessä.
Ensin asennetaan tarvittava moduuli.
% % bash
pip install datasketch - U
Collecting datasketch Downloading https://files.pythonhosted.org/packages/d4/28/384b1a1129705df345961e778c85d72efdc05609a2373c7a867d73227304/datasketch-1.2.7-py2.py3-none-any.whl (63kB) Requirement already satisfied, skipping upgrade: numpy>=1.11 in /home/petteri/anaconda3/envs/ml/lib/python3.6/site-packages (from datasketch) (1.14.5) Collecting redis>=2.10.0 (from datasketch) Downloading https://files.pythonhosted.org/packages/3b/f6/7a76333cf0b9251ecf49efff635015171843d9b977e4ffcf59f9c4428052/redis-2.10.6-py2.py3-none-any.whl (64kB) Installing collected packages: redis, datasketch Successfully installed datasketch-1.2.7 redis-2.10.6
Tämän jälkeen koulutetaan MinHash-malli käyttämällä roskaposteiksi luokiteltuja sähköposteja koulutusdatana. Jokaisesta sellaisesta lasketaan tiiviste, joka sitten syötetään tiivisteiden samankaltaisuuden huomioivaan (locality-sensitive hashing, LSH) malliin.
from datasketch import MinHash, MinHashLSH
spam_files = [x for x in x_train if labels[x] == 0]
lsh_spam = MinHashLSH(threshold=0.5, num_perm=128)
for file in spam_files:
minhash = MinHash(num_perm=128)
stems = email_utils.load_stems(os.path.join(DATA_DIR, file))
if len(stems) > 1:
[minhash.update(stem.encode('utf-8')) for stem in stems]
lsh_spam.insert(file, minhash)
Tämän jälkeen testataan mallin kyvykkyyttä testidatalla. Kuten mustalistauksenkin kanssa, kyvykkyyttä tutkitaan sekaannusmatriisilla.
true_pos, true_neg, false_pos, false_neg = 0, 0, 0, 0
for file in x_test:
if file in labels:
minhash = MinHash(num_perm=128)
label, stems = labels[file], email_utils.load_stems(
os.path.join(DATA_DIR, file))
if len(stems) > 1:
[minhash.update(stem.encode('utf-8')) for stem in stems]
model_matches = lsh_spam.query(minhash)
if model_matches: # Malli: Spam
if label == 1:
false_neg += 1
else:
true_neg += 1
else: # Malli: Mail
if label == 1:
true_pos += 1
else:
false_pos += 1
results = pd.DataFrame(data=[[true_neg, false_pos], [false_neg, true_pos]],
index=['model_0', 'model_1'], columns=['true_0', 'true_1'])
results_p = results/results.sum().sum()*100
print("Tulokset määrissä:")
display(results)
print("Tulokset prosenteissa:")
display(results_p)
print(
f'Mallin tarkkuus (ON+OP): {results_p.iloc[0,0]+results_p.iloc[1,1]:.3f}%')
Tulokset määrissä:
true_0 | true_1 | |
---|---|---|
model_0 | 24109 | 6820 |
model_1 | 231 | 17293 |
Tulokset prosenteissa:
true_0 | true_1 | |
---|---|---|
model_0 | 49.757497 | 14.075496 |
model_1 | 0.476751 | 35.690257 |
Mallin tarkkuus (ON+OP): 85.448%
Roskapostien tiivisteiden samankaltaisuuteen perustuva malli saavutti jo huomattavasti korkeamman luokittelutuloksen.
Viimeinen ensimmäisen luvun esimerkissä implementoitu malli on nimeltään Naiivi Bayesin luokittelija (Naive Bayes classifier). Sähköpostien kantasanojen mukaan luokittelu toimii vain niin pitkälle, kun datasta löytyy riittävän paljon opittavaa dataa etenkin roskaposteista. Malli koulutetaan syöttämällä sille sähköpostit lähestulkoon raakadatana ja kertomalla sille kunkin sähköpostin oikea vastaus (spam/ham). Raakadata on tosin ehkä hieman harhaanjohtava käsite, sillä sanoista muodostetaan sanojen säkki (bag of words). Siinä jokaisella sanalla on oma sarakkeensa. Kullekin viestille muodostetaan tämän jälkeen vektori, jossa on lukumäärä kullekin sanalle (0 ... n
).
Kuten edellä, tässäkin esimerkissä käytetään mustalistauksen muuttujia.
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics import confusion_matrix
x, y = [], []
for file in data:
email = email_utils.extract_email_text(os.path.join(DATA_DIR, file))
x.append(email), y.append(labels[file])
x_train, x_test, y_train, y_test = train_test_split(
x, y, test_size=1-TRAINING_SET_RATIO)
vectorizer = CountVectorizer()
x_train_vector = vectorizer.fit_transform(x_train)
x_test_vector = vectorizer.transform(x_test)
model = MultinomialNB()
model.fit(x_train_vector, y_train)
y_pred = model.predict(x_test_vector)
results = pd.DataFrame(data=confusion_matrix(y_test, y_pred),
index=['model_0', 'model_1'], columns=['true_0', 'true_1'])
results_p = results/results.sum().sum()*100
print("Tulokset määrissä:")
display(results)
print("Tulokset prosenteissa:")
display(results_p)
print(
f'Mallin tarkkuus (ON+OP): {results_p.iloc[0,0]+results_p.iloc[1,1]:.3f}%')
Tulokset määrissä:
true_0 | true_1 | |
---|---|---|
model_0 | 14138 | 880 |
model_1 | 160 | 7448 |
Tulokset prosenteissa:
true_0 | true_1 | |
---|---|---|
model_0 | 62.485636 | 3.889331 |
model_1 | 0.707151 | 32.917882 |
Mallin tarkkuus (ON+OP): 95.404%
Kyseinen malli toimi vieläkin paremmin parantaen tarkkuutta jo hyvin korkealle. Vieläkin parempaan luokittelutulokseen päästäisiin, mikäli jo koulutetuista malleista muodostettaisiin monesta mallista koostuva ensemble.
Todellisuudessa edes sähköpostien luokittelu ei ole kovinkaan yksinkertaista, sillä hyökkääjät kehittävät menetelmiään samaan tahtiin puolustajien kanssa. Ja yleisemminkin koneoppimisellakin on rajoitteensa, myös tietoturva-asioissakin. Todellisuudessa luokittelutarkkuutta kiinnostavampia asioita voivat olla aivan muut asiat, kuten selittävät tekijät. Näihin taas on mallista riippuen erityisenkin vaikeaa päästä käsiksi.
Koneoppimismenetelmät ovat myös kovin tapauskohtaisia. Jossain toimiva malli ei ole taatusti toimiva jossain toisessa tilanteessa. Samoin menetelmille ei voida antaa koko kuvaa käsiteltäväksi - tämä rajaa suuren osan hiljaisesta, kulttuurisesta ja pitkältä eri tilanteista opitusta tiedosta mallintamisen ulkopuolelle. Ihmiset eivät katso vain sähköpostin sanoja, vaan myös monia muitakin tekijöitä, jotka eivät suoranaisesti edes ole johdettavissa sähköpostin sisällöstä.
Samoin mallit voivat oppia toimimaan erittäinkin tehokkaasti koulutuksen aikana, mutta ns. tuotantokäytössä osoittautuvat täysin käyttökelvottomiksi. Tällöin malli oppinut koulutusdatan lähes täysin, milloin sen sanotaan ylisovittuneen (overfit). Näin sovittunutta mallia ei voi käyttää uusien, sen ennalta havaitsemattomien havaintojen luotettavaan tunnistamiseen.
Joten vaikka koneoppiminen onkin tämän kirjan pääaiheita, ei sillä voida kuitenkaan jokaista ongelmaa ratkaista.