--- title: ScamShield emoji: πŸ›‘οΈ colorFrom: red colorTo: blue sdk: docker app_port: 7860 pinned: true license: mit --- # ScamShield β€” Technical Report ## Multilingual Smishing Detection: XLM-RoBERTa + URL Fusion + Mobile Deployment **Base paper:** "Enhancing Smishing Detection: A Deep Learning Approach for Improved Accuracy and Reduced False Positives" β€” IEEE Access, 2024 (DOI: 10.1109/ACCESS.2024.3463871) --- ## 1. Introduction & Problem Statement ### 1.1 What is Smishing? Smishing (SMS Phishing) is a social engineering attack where fraudulent SMS messages trick recipients into revealing sensitive information β€” passwords, OTPs, bank account details β€” by impersonating trusted entities (banks, delivery services, government agencies). ### 1.2 Why Detection is Hard - SMS messages are short (<160 chars) β€” limited context - Attackers continuously evolve language to evade filters - **Legitimate Indian transactional SMS (OTP, bank credits, recharges) resembles spam patterns** β€” high false positive risk - Class imbalance: ~61% ham, ~39% spam - Adversarial evasion: character substitution, spacing tricks, word manipulation ### 1.3 Our Contributions Over the Base Paper | Contribution | Base Paper (CNN-LSTM) | Our System | |---|---|---| | Model | CNN-LSTM from scratch | **XLM-RoBERTa** (multilingual pre-trained transformer) | | Languages | English only | **English + Hindi + Hinglish** | | URL Analysis | None | **9 URL risk signals + Google Safe Browsing** | | Explainability | None | **SHAP word-level explanations** | | Adversarial Testing | None | **4 attack types tested** | | Training Data | ~5,574 messages | **~30,000+ messages (6 sources, multilingual)** | | Mobile App | None | **React Native Android/iOS app with real-time SMS scanning** | | Indian SMS Support | None | **60+ synthetic Indian legit SMS + feature fixes** | | Encryption | None | **AES-256-CBC end-to-end encrypted API channel** | | Real-time Monitoring | None | **Background SMS polling with push notifications** | --- ## 2. System Architecture ### 2.1 Three-Component System ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ ScamShield System β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” AES-256-CBC β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ ScamShield │◄─────────────►│ Flask API β”‚ β”‚ β”‚ β”‚ Mobile App β”‚ /predict_ β”‚ (smishing_detector) β”‚ β”‚ β”‚ β”‚ (React Native) β”‚ secure β”‚ Port 5000 β”‚ β”‚ β”‚ β”‚ Android / iOS β”‚ /explain β”‚ β”‚ β”‚ β”‚ β”‚ Real-time SMS β”‚ /health β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β–² Polls every 15s β”‚ β”‚ β”‚ β”‚ (android inbox, latest 30) β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ KaggleTraining/ │── best_model.pt ─────────►│ β”‚ β”‚ β”‚ (Isolated pkg) β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Google Safe Browsing β”‚ β”‚ β”‚ β”‚ API (URL Verification)β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### 2.2 Model Pipeline ``` SMS Message (English / Hindi / Hinglish) β”‚ β”œβ”€β”€β–Ί XLM-RoBERTa Tokenizer β†’ XLM-RoBERTa Encoder β†’ CLS Token [768-d] β”‚ (SentencePiece, handles Devanagari natively) β”‚ β”œβ”€β”€β–Ί URL Feature Extractor β†’ 9 URL signals ──┐ β”‚ β”‚ β–Ό β–Ό └──► Text Feature Extractor β†’ 8 signals β†’ feat_proj β†’ [64-d] β”‚ Concatenate [832-d] β”‚ Classifier MLP (832β†’256β†’64β†’1) β”‚ Sigmoid β†’ P(spam) β”‚ β”Œβ”€β”€β”€β”€β”€β”€ β‰₯ 0.55? ────── β–Ό β–Ό SPAM/MEDIUM HAM/LOW β”‚ Has URLs? β”‚ Yes β–Ό Google Safe Browsing All URLs clean? β†’ Override to HAM ``` ### 2.3 Project Structure ``` MAIN-EL-2/ β”œβ”€β”€ smishing_detector/ ← Flask API + model inference β”‚ β”œβ”€β”€ app/flask_api.py ← REST API (5 endpoints) β”‚ β”œβ”€β”€ predictor.py ← Inference + GSB override β”‚ β”œβ”€β”€ models/model.py ← SmishingDetector nn.Module β”‚ β”œβ”€β”€ models/dataset.py ← PyTorch Dataset β”‚ β”œβ”€β”€ utils/data_loader.py ← Feature engineering β”‚ β”œβ”€β”€ utils/safe_browsing.py ← Google Safe Browsing client β”‚ β”œβ”€β”€ explainability/ ← SHAP explainer β”‚ β”œβ”€β”€ adversarial/ ← Robustness testing β”‚ └── best_model.pt ← Trained checkpoint (~266 MB) β”œβ”€β”€ ScamShield-Mobile/ ← React Native mobile app β”‚ β”œβ”€β”€ App.js ← Root + theme β”‚ β”œβ”€β”€ src/screens/ ← Inbox, Scan, Detail, Settings β”‚ β”œβ”€β”€ src/components/ ← RiskBadge, ShapChart, ConfidenceBar… β”‚ └── src/services/api.js ← Flask API client β”œβ”€β”€ KaggleTraining/ ← Isolated Kaggle training package β”‚ β”œβ”€β”€ train.py ← Training entry point β”‚ β”œβ”€β”€ model.py ← Architecture (same as API) β”‚ β”œβ”€β”€ dataset.py ← DataLoaders β”‚ └── data_loader.py ← Feature engineering (fixed) β”œβ”€β”€ .env ← API keys (GSB + Kaggle) └── COMMANDS_REFERENCE.md ``` --- ## 3. Technologies Used ### 3.1 Core Stack | Layer | Technology | Purpose | |---|---|---| | Deep Learning | **PyTorch β‰₯ 2.0** | Model training, inference | | Transformer | **HuggingFace Transformers β‰₯ 4.35** | XLM-RoBERTa model + tokenizer | | NLP Model | **xlm-roberta-base** | Pre-trained multilingual encoder (270M params, 100 languages) | | Tokenizer | **SentencePiece** | Handles Devanagari, Roman, English natively | | Data Science | **scikit-learn, pandas, NumPy** | Metrics, splitting, normalization | | Explainability | **SHAP β‰₯ 0.43** | Word-level feature attribution | | URL Analysis | **tldextract, requests** | Domain/TLD extraction | | API | **Flask β‰₯ 3.0 + flask-cors** | REST backend | | Mobile | **React Native (Expo SDK 54)** | Cross-platform mobile app | | Mobile Nav | **React Navigation v7** | Tab + stack navigation | | Mobile Storage | **AsyncStorage** | Scan history, settings | | Security | **Google Safe Browsing API v4** | URL threat verification | | GPU | **Kaggle T4** | Training (via KaggleTraining package) | ### 3.2 Why XLM-RoBERTa over DistilBERT? | Aspect | DistilBERT (Phase 2) | XLM-RoBERTa (Phase 3) | |---|---|---| | Languages | English only | 100 languages (Hindi, Urdu, Bengali...) | | Parameters | 66M | 270M | | Pre-training data | English Wikipedia + BooksCorpus | 2.5TB CommonCrawl (100 languages) | | Hindi support | ❌ None | βœ… Native Devanagari via SentencePiece | | Hinglish support | ❌ Fragmented | βœ… Handles Roman-script Hindi | | Accuracy (English) | ~99.66% | β‰₯97% (target, larger model needs more data) | | Model size | 250MB | 1.1GB | --- ## 4. Model Architecture ### 4.1 SmishingDetector (Phase 3) ```python SmishingDetector( bert: XLMRobertaModel ← xlm-roberta-base, all 12 layers trainable feat_proj: Sequential( Linear(17 β†’ 64), ReLU(), Dropout(0.3), Linear(64 β†’ 64), ReLU() ) classifier: Sequential( Linear(832 β†’ 256), ReLU(), Dropout(0.3), Linear(256 β†’ 64), ReLU(), Dropout(0.3), Linear(64 β†’ 1) ← single logit ) ) ``` **Input dimension:** 17 hand-crafted features (9 URL + 8 text) **Fusion:** CLS [768] + feat_proj [64] = **[832-d]** **Output:** sigmoid(logit) β†’ P(spam) ∈ [0, 1] ### 4.2 Feature Engineering (v2 β€” Fixed) #### URL Features (9 signals) | Feature | Description | |---|---| | `has_url` | Message contains a URL | | `num_urls` | URL count | | `has_http` | Insecure HTTP | | `has_https` | HTTPS present | | `suspicious_tld` | `.tk`, `.xyz`, `.ml`, `.loan`, etc. | | `max_url_len` | Longest URL length | | `has_ip_url` | Raw IP address URL | | `has_shortened_url` | `bit.ly`, `t.co`, etc. | | `has_legit_domain` | Domain in whitelist OR cleared by GSB | #### Text Features (8 signals) β€” v2 fixes highlighted | Feature | Description | v2 Change | |---|---|---| | `num_chars` | Character count | β€” | | `num_words` | Word count | β€” | | `pct_upper` | % uppercase | β€” | | `pct_digits` | % digits | β€” | | `num_special` | Special char count | β€” | | `urgency_count` | Urgency keyword matches | **Removed `account`, `verify`, `otp`** β€” too common in legit Indian SMS | | `has_phone` | Contains phone number | **Fixed regex for +91 / 10-digit Indian format** | | `has_currency` | Currency detected | **Removed `rs`, `rupee` text match β€” only `β‚Ή` symbol now** | --- ## 5. Training β€” v2 (Kaggle) ### 5.1 Configuration | Parameter | Phase 2 (DistilBERT) | Phase 3 (XLM-RoBERTa) | Rationale | |---|---|---|---| | Learning Rate | 2e-5 | **1e-5** | Stable fine-tuning of larger model | | Dropout | 0.4 | **0.3** | Larger model, less aggressive dropout | | Frozen BERT layers | 3 | **0** | Full fine-tuning needed for multilingual | | Batch size | 32 | **16** | XLM-RoBERTa uses more VRAM | | pos_weight multiplier | 1.5Γ— | **1.0Γ—** | No artificial spam bias | | Decision threshold | 0.50 | **0.55** | Reduce false positives on Indian SMS | | Label smoothing | None | **0.05** | Prevents overconfident predictions | | Early stop patience | 3 | **4** | More time to generalize | | Max epochs | 8 | **10** | β€” | | Training datasets | 4 sources | **6 sources (+ Hindi/Hinglish)** | Multilingual coverage | ### 5.2 Label Smoothing Loss Standard BCE was replaced with a custom `LabelSmoothingBCELoss`: ``` targets_smooth = targets Γ— (1 - Ξ΅) + Ξ΅ Γ— 0.5 ``` With `Ξ΅ = 0.05`: spam labels become `0.975` (not `1.0`) and ham labels become `0.025` (not `0.0`). This prevents the model from becoming overconfident and generalizes better. ### 5.3 Dataset (v2) ``` Source Messages Notes ────────────────────────────────────────────────────────────────── UCI SMS Spam Collection ~5,572 Gold standard Deysi/spam-detection (HuggingFace) ~10,900 Large, diverse gauravduttakiit/sms-spam (Kaggle) ~varies Indian SMS context Synthetic Indian Legit SMS 60 Hand-crafted OTP/bank dbarbedillo multilingual (en+hi columns) ~11,144 Hindi + English rajnathpatel/multilingual-spam-data ~varies Real Hindi/Hinglish ────────────────────────────────────────────────────────────────── After deduplication: ~30,000+ Split: 70% train / 15% val / 15% test (stratified) ``` **Why synthetic Indian SMS?** All 3 original datasets are Western English. The model had never seen legitimate Indian bank credits, OTP messages, or recharge confirmations β€” so it flagged everything with `Rs.`, `HDFC`, `credited` as spam. ### 5.4 Root Cause of Overfitting (v1) The original model marked every Indian transactional SMS as high-risk spam (99.9% confidence) because: 1. **Distribution mismatch** β€” zero legitimate Indian SMS in training data 2. **`has_currency` fired on `Rs.`** β€” every bank SMS triggered it 3. **`urgency_count` fired on `account`, `verify`** β€” every bank SMS triggered it 4. **All BERT layers unfrozen** β€” model memorized training corpus patterns aggressively 5. **pos_weight 1.5Γ—** β€” artificially pushed predictions toward spam --- ## 6. Google Safe Browsing Integration ### 6.1 How It Works ``` Model Prediction: SPAM (confidence 0.82) β”‚ └── Message has URLs? Yes β”‚ β–Ό Extract all URLs β”‚ β–Ό Query Google Safe Browsing API v4 (MALWARE, SOCIAL_ENGINEERING, UNWANTED_SOFTWARE) β”‚ All URLs clean? Yes ──────────────► Override β†’ HAM / LOW risk gsb_cleared = true in response No / Error ───────► Keep model prediction ``` ### 6.2 API Response with GSB ```json { "label": "ham", "confidence": 0.45, "risk_level": "low", "gsb_cleared": true, "url_signals": { ... }, "text_signals": { ... } } ``` ### 6.3 Bug Fixed in v1 The original `safe_browsing.py` had an **inverted cache logic** β€” when GSB returned "no threats found" (domain is safe), it was storing `False` in the cache, meaning every GSB-verified clean domain was still treated as dangerous. This has been fixed. --- ## 7. Evaluation Results (v1 Model) > Note: v2 results will be available after Kaggle retraining. ### 7.1 Core Metrics | Metric | Our System (v1) | Paper (CNN-LSTM) | Improvement | |---|---|---|---| | Accuracy | **99.66%** | 97.49% | +2.17% | | Precision (spam) | **99.46%** | ~97% | +2.46% | | Recall (spam) | **99.67%** | ~97% | +2.67% | | F1 (spam) | **99.57%** | 0.97 | +2.57% | | False Positive Rate | **0.34%** | ~3% | 8.8Γ— lower | | ROC-AUC | **0.9999** | β€” | β€” | | MCC | **0.9929** | β€” | β€” | ### 7.2 Confusion Matrix (v1, test set n=2,373) ``` Predicted Ham Spam Actual Ham [1446 5] ← 5 false alarms Spam [ 3 919] ← 3 missed ``` ### 7.3 Adversarial Robustness | Attack | Method | F1 Drop | |---|---|---| | CharSwap | Replace letters with l33t-speak (30% rate) | **0.00** | | EDA | Random word deletion + swap (20% rate) | **0.00** | | Spacing | Insert spaces in keywords | **0.00** | | Hybrid | All three combined | **0.00** | Zero degradation β€” DistilBERT's subword tokenization is inherently robust to surface-level text manipulations. --- ## 8. Mobile Application ### 8.1 Overview React Native (Expo) cross-platform app providing real-time SMS analysis on Android and manual scanning on iOS. ### 8.2 Screens | Screen | Description | |---|---| | **Inbox** | SMS message list with risk badges; stats card (Scanned/Threats/Safe); Scan All button | | **Scan** | Manual message input + URL extractor; full analysis on submit | | **Detail** | SHAP chart, confidence bar, URL analysis, text signals, threat warnings, GSB badge | | **Settings** | API URL config + connectivity test, auto-scan toggle, dark/light theme, history management | ### 8.3 Key Components | Component | Purpose | |---|---| | `RiskBadge` | Color-coded pill (green=low, amber=medium, red=high) | | `ConfidenceBar` | Animated probability bar | | `ShapChart` | Horizontal bar chart of top spam/ham word contributions | | `UrlAnalysis` | Per-URL safety breakdown with risk indicators | ### 8.4 API Integration ``` Mobile App β†’ POST /predict β†’ Risk label, confidence, signals, gsb_cleared β†’ POST /explain β†’ SHAP top_spam_words, top_ham_words β†’ POST /check-domain β†’ Google Safe Browsing result β†’ GET /health β†’ API connectivity check ``` ### 8.5 Build ```bash eas build --platform android --profile preview # β†’ .apk eas build --platform android --profile production # β†’ signed .apk ``` --- ## 9. API Endpoints | Endpoint | Method | Input | Output | |---|---|---|---| | `/health` | GET | β€” | `{status, model}` | | `/predict` | POST | `{message}` | `{label, confidence, risk_level, gsb_cleared, url_signals, text_signals}` | | `/explain` | POST | `{message}` | `{label, confidence, top_spam_words, top_ham_words, feature_importances}` | | `/batch_predict` | POST | `{messages[]}` | `{results[], count}` | | `/check-domain` | POST | `{domain}` | `{domain, is_legitimate, status}` | --- ## 9. Security & Mobile Architecture ### 9.1 AES-256-CBC End-to-End Encryption All SMS content sent from the mobile app to the Flask API is encrypted using **AES-256-CBC** before transmission. This protects sensitive message content from interception (e.g., on shared Wi-Fi or untrusted networks). **Encryption Flow:** ``` Mobile (React Native) Server (Flask API) ────────────────────── ────────────────── 1. Read SMS from inbox 1. Receive POST /predict_secure 2. Generate random 16-byte IV 2. Base64-decode payload 3. AES-256-CBC encrypt(message, key, IV) 3. Extract IV (first 16 bytes) 4. Prepend IV to ciphertext 4. AES-256-CBC decrypt(ciphertext, key, IV) 5. Base64-encode β†’ send to API 5. Run XLM-RoBERTa prediction ``` **Key Management:** - 256-bit key stored in server `.env` as `SMS_ENCRYPTION_KEY` - Mobile fetches key from `/api/encryption-key` on first launch (token-protected via `X-App-Token` header) - Key cached in device `AsyncStorage` for offline use - Default fallback key ensures operation even if API is temporarily unreachable **Libraries Used:** - Mobile: `crypto-js` (AES-CBC, PKCS7 padding) - Server: `cryptography` (Python, `hazmat.primitives.ciphers`) ### 9.2 Real-Time SMS Monitoring The mobile app monitors the Android SMS inbox in real-time using a two-tier approach: **Foreground Monitoring (while app is open):** - Polls the Android SMS inbox every **15 seconds** using `react-native-get-sms-android` - Reads only the **latest 30 messages** (configurable) to minimize memory usage - New messages since last check are auto-scanned via `/predict_secure` **Background Monitoring (app closed):** - Uses `expo-background-fetch` + `expo-task-manager` to register a persistent background task - Android schedules background fetches when device is idle (typically every 15 min) - Task auto-scans new SMS and fires a **local push notification** if risk level is `high` or `medium` **Notification Payload:** ``` ⚠️ ScamShield: Suspicious SMS Detected From +91-XXXXX: "Aapka electricity connection aaj raat..." Confidence: 97% ``` **Permissions Required (Android):** - `READ_SMS` β€” read inbox contents - `RECEIVE_SMS` β€” be notified of new messages - `RECEIVE_BOOT_COMPLETED` β€” restart monitoring after device reboot ### 9.3 API Endpoints Summary | Endpoint | Method | Auth | Description | |---|---|---|---| | `/predict` | POST | None | Unencrypted prediction (fallback) | | `/predict_secure` | POST | None | AES-256-CBC encrypted prediction | | `/batch_predict` | POST | None | Batch predict multiple messages | | `/explain` | POST | None | SHAP explanation | | `/check-domain` | POST | None | Google Safe Browsing lookup | | `/api/encryption-key` | GET | X-App-Token | Returns AES key for mobile | | `/health` | GET | None | Model status | --- ## 10. Key Design Decisions | Decision | Rationale | |---|---| | XLM-RoBERTa over DistilBERT | 100-language support, Devanagari native, same 768-d hidden size | | All layers unfrozen | Multilingual fine-tuning needs full gradient flow through all 12 layers | | Late fusion (concatenation) | BERT and hand-crafted features learn independently before combining | | 17 hand-crafted features | Language-agnostic URL signals that XLM-RoBERTa alone cannot extract | | GSB whitelist-only override | Only known-good domains override spam β€” new phishing domains not in GSB DB | | Threshold 0.55 (not 0.50) | Reduces false positives on borderline cases (Indian bank SMS) | | Label smoothing 0.05 | Prevents overconfident predictions on training distribution | | Batch size 16 (not 32) | XLM-RoBERTa (1.1GB) needs more VRAM per forward pass than DistilBERT | | Stratified 70/15/15 split | Maintains spam/ham ratio across all data splits | | Normalization from train only | Prevents data leakage from val/test into normalization statistics | | Synthetic Indian SMS | Corrects training distribution bias against Indian transactional messages | | AES-256-CBC (not AES-GCM) | `crypto-js` (React Native) natively supports CBC; simpler interop with Python | | Latest 30 SMS only | Limits memory usage and inference time in background task | --- ## 11. Hardware & Performance | Component | Spec | |---|---| | GPU (training) | Kaggle T4 (via KaggleTraining package) | | RAM | 16 GB recommended | | Storage | ~1.3 GB (model ~1.1 GB + cached XLM-RoBERTa weights) | | Training time | ~45–90 min (Kaggle T4) | | Inference latency | ~100 ms/message (GPU), ~500 ms (CPU) | | API response time | ~600 ms (includes GSB lookup) | | Encryption overhead | <5 ms (AES-256-CBC, negligible) | --- ## 12. Results Summary | Metric | Value | |---|---| | Test Accuracy | **97.54%** | | Spam F1-Score | **0.94** | | Val F1 (best epoch) | **0.9765** | | False Positive Rate | **0.46%** | | Hindi F1 (5,572 msgs) | **0.9845** | | Adversarial F1 drop | **≀ 0.01** | | Manual test (12 cases) | **12/12 correct** | --- ## 13. Future Work 1. **On-device inference** β€” Export to ONNX/TFLite for fully offline mobile prediction (no API needed) 2. **Active URL scanning** β€” Follow redirects, analyze landing page content 3. **More Indian languages** β€” Tamil, Telugu, Kannada, Bengali via IndicBERT 4. **Federated learning** β€” Train across devices without centralizing SMS data 5. **Continuous learning** β€” Periodic model updates from newly reported scam patterns 6. **Domain age check** β€” WHOIS lookup as additional URL feature (newly registered domains = higher risk) 7. **iOS support** β€” SMS reading on iOS requires SiriKit/Message Filter Extension entitlement