{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# ๐ก๏ธ Malicious URL HyperModel\n", "\n", "**Dataset:** [Malicious URLs](https://www.kaggle.com/datasets/sid321axn/malicious-urls-dataset) โ 651K URLs ยท 4 classes (benign / phishing / defacement / malware)\n", "\n", "**Objective:** Build a GPU-accelerated stacking HyperModel that outperforms literature baselines:\n", "\n", "| Baseline | Accuracy | Source |\n", "|---|---|---|\n", "| RF on URL features | 97.98% | Sahingoz 2019 |\n", "| URLNet (Char+Word CNN) | ~98.5% | Le 2018 |\n", "| Stacking CNN+LSTM+GRU | 97.5% | Nguyen 2021 |\n", "| CharCNN | 96.75% | Aljofey 2022 |\n", "\n", "**Architecture:**\n", "```\n", "Raw URLs (651K)\n", " โโโ Feature Engineering (54 lexical features)\n", " โ โโโ XGBoost (GPU)\n", " โ โโโ LightGBM (CPU)\n", " โโโ TF-IDF char n-grams โ Logistic Regression\n", " โโโ Character-level Deep Learning (GPU)\n", " โโโ CharCNN\n", " โโโ CharBiLSTM\n", " โโโ CNN-BiLSTM Hybrid\n", " โ\n", " โผ\n", "Stacking Meta-Learner (XGBoost GPU) โ Final Prediction\n", "```\n", "\n", "See [LITERATURE_REVIEW.md](LITERATURE_REVIEW.md) for a full survey of 15+ research papers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 0. Install & Setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.0\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "PyTorch 2.10.0+cu128 | CUDA available: True\n", " GPU: NVIDIA L4\n", " GPUs: 1\n", "\n", "โ Packages ready\n" ] } ], "source": [ "# โโโ Install Dependencies โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n", "import subprocess, sys\n", "\n", "def pip_install(pkg):\n", " subprocess.check_call([sys.executable, '-m', 'pip', 'install', pkg, '-q'])\n", "\n", "for pkg in [\n", " 'xgboost>=2.0', 'lightgbm>=4.0',\n", " 'optuna', 'kagglehub',\n", " 'tldextract', 'imbalanced-learn',\n", " 'seaborn', 'plotly', 'pyarrow',\n", "]:\n", " try:\n", " pip_install(pkg)\n", " except Exception as e:\n", " print(f'Warning: {pkg}: {e}')\n", "\n", "# Verify PyTorch CUDA\n", "import torch\n", "print(f'PyTorch {torch.__version__} | CUDA available: {torch.cuda.is_available()}')\n", "if torch.cuda.is_available():\n", " print(f' GPU: {torch.cuda.get_device_name(0)}')\n", " print(f' GPUs: {torch.cuda.device_count()}')\n", "print('\\n\\u2705 Packages ready')" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Project root : /teamspace/studios/this_studio/urlproject\n", "Data dir : /teamspace/studios/this_studio/urlproject/data\n", "Output dir : /teamspace/studios/this_studio/urlproject/outputs\n" ] } ], "source": [ "import sys, os, pathlib, warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "PROJECT_ROOT = pathlib.Path('.').resolve()\n", "sys.path.insert(0, str(PROJECT_ROOT))\n", "\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "sns.set_theme(style='whitegrid', palette='muted')\n", "pd.set_option('display.max_columns', 30)\n", "pd.set_option('display.float_format', '{:.4f}'.format)\n", "\n", "SEED = 42\n", "np.random.seed(SEED)\n", "\n", "DATA_DIR = PROJECT_ROOT / 'data'\n", "OUTPUT_DIR = PROJECT_ROOT / 'outputs'\n", "DATA_DIR.mkdir(parents=True, exist_ok=True)\n", "OUTPUT_DIR.mkdir(parents=True, exist_ok=True)\n", "\n", "print(f'Project root : {PROJECT_ROOT}')\n", "print(f'Data dir : {DATA_DIR}')\n", "print(f'Output dir : {OUTPUT_DIR}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Download Dataset" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "โ Dataset already exists: /teamspace/studios/this_studio/urlproject/data/malicious_phishing.csv\n", "\n", "Dataset path: /teamspace/studios/this_studio/urlproject/data/malicious_phishing.csv\n" ] } ], "source": [ "from download_data import download_malicious_urls\n", "\n", "csv_path = download_malicious_urls(DATA_DIR)\n", "print(f'\\nDataset path: {csv_path}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Exploratory Data Analysis" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Shape : (651191, 2)\n", "Columns : ['url', 'type']\n", "\n", "Class distribution:\n", "type\n", "benign 428103\n", "defacement 96457\n", "phishing 94111\n", "malware 32520\n", "Name: count, dtype: int64\n" ] }, { "data": { "text/html": [ "
| \n", " | url | \n", "type | \n", "
|---|---|---|
| 0 | \n", "br-icloud.com.br | \n", "phishing | \n", "
| 1 | \n", "mp3raid.com/music/krizz_kaliko.html | \n", "benign | \n", "
| 2 | \n", "bopsecrets.org/rexroth/cr/1.htm | \n", "benign | \n", "
| 3 | \n", "http://www.garage-pirenne.be/index.php?option=... | \n", "defacement | \n", "
| 4 | \n", "http://adventure-nicaragua.net/index.php?optio... | \n", "defacement | \n", "
| \n", " | url | \n", "url_length | \n", "hostname_length | \n", "domain_length | \n", "path_length | \n", "query_length | \n", "fragment_length | \n", "tld_length | \n", "num_dots | \n", "num_hyphens | \n", "num_underscores | \n", "num_slashes | \n", "num_at_signs | \n", "num_ampersands | \n", "num_equals | \n", "... | \n", "domain_token_count | \n", "has_suspicious_tld | \n", "domain_has_digits | \n", "domain_hyphen_count | \n", "phishing_keyword_count | \n", "brand_count | \n", "has_suspicious_ext | \n", "brand_in_subdomain | \n", "suspicious_word_ratio | \n", "path_depth | \n", "longest_path_token | \n", "avg_path_token_len | \n", "max_consecutive_digits | \n", "path_entropy | \n", "label | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", "br-icloud.com.br | \n", "16.0000 | \n", "16.0000 | \n", "9.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "6.0000 | \n", "2.0000 | \n", "1.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "... | \n", "4.0000 | \n", "0.0000 | \n", "0.0000 | \n", "1.0000 | \n", "1.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.2500 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "phishing | \n", "
| 1 | \n", "mp3raid.com/music/krizz_kaliko.html | \n", "35.0000 | \n", "11.0000 | \n", "7.0000 | \n", "24.0000 | \n", "0.0000 | \n", "0.0000 | \n", "3.0000 | \n", "2.0000 | \n", "0.0000 | \n", "1.0000 | \n", "2.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "... | \n", "2.0000 | \n", "0.0000 | \n", "1.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "2.0000 | \n", "17.0000 | \n", "11.0000 | \n", "1.0000 | \n", "3.8554 | \n", "benign | \n", "
| 2 | \n", "bopsecrets.org/rexroth/cr/1.htm | \n", "31.0000 | \n", "14.0000 | \n", "10.0000 | \n", "17.0000 | \n", "0.0000 | \n", "0.0000 | \n", "3.0000 | \n", "2.0000 | \n", "0.0000 | \n", "0.0000 | \n", "3.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "... | \n", "2.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "0.0000 | \n", "3.0000 | \n", "7.0000 | \n", "4.6667 | \n", "1.0000 | \n", "3.2928 | \n", "benign | \n", "
3 rows ร 56 columns
\n", "