my_xgb_api / app.py
genee123's picture
FINAL NA TALAGA TO
915113d verified
import numpy as np
import pandas as pd
import re
import gradio as gr
import requests
import json
import xgboost as xgb
import joblib
from urllib.parse import urlparse
import base64
feature_names = joblib.load('feature_names.joblib')
non_numeric_columns = joblib.load('non_numeric_columns.joblib')
scaler = joblib.load('scaler.joblib')
label_encoders = {}
for col in non_numeric_columns:
try:
le = joblib.load(f'le_{col}.joblib')
label_encoders[col] = le
except:
pass
# Load XGBoost model
model = xgb.Booster()
model.load_model('xgboost_best_model.json')
# API
GOOGLE_API_KEY = "AIzaSyCmXigpl6t7MZyvq8Rv37XW_lZVCi0oMSM"
PHISHTANK_USER_AGENT = "phishtank/PHISH"
VIRUSTOTAL_API_KEY = "7c38bbc8b0f461b25b38d1cda16404fc8c3997c6b569a257707505759da7a996"
VIRUSTOTAL_BASE_URL = "https://www.virustotal.com/api/v3/"
vt_headers = {
"x-apikey": VIRUSTOTAL_API_KEY,
"Accept": "application/json"
}
def check_url_virustotal(target_url):
try:
url_id = base64.urlsafe_b64encode(target_url.encode()).decode().strip("=")
url = f"{VIRUSTOTAL_BASE_URL}urls/{url_id}"
response = requests.get(url, headers=vt_headers, timeout=10)
if response.status_code == 200:
data = response.json()
malicious_count = 0
phishing_keywords = ["phishing", "malicious"]
for vendor, result_data in data['data']['attributes']['last_analysis_results'].items():
if result_data['result'] and any(keyword in result_data['result'].lower() for keyword in phishing_keywords):
malicious_count += 1
if malicious_count >= 5:
return "Phishing"
else:
return "Legitimate"
else:
return "Legitimate"
except:
return "Legitimate"
def check_url_phishtank(url):
endpoint = "https://checkurl.phishtank.com/checkurl/"
payload = {"url": url, "format": "json"}
headers = {"User-Agent": PHISHTANK_USER_AGENT}
try:
response = requests.post(endpoint, data=payload, headers=headers, timeout=5)
response.raise_for_status()
data = response.json()
if data.get('results', {}).get('in_database', False) and data.get('results', {}).get('verified', False):
return "Phishing"
return "Legitimate"
except Exception as e:
return "Legitimate"
def check_url_safebrowsing(url):
endpoint = f"https://safebrowsing.googleapis.com/v4/threatMatches:find?key={GOOGLE_API_KEY}"
payload = {
"client": {"clientId": "PhishShield", "clientVersion": "1.0"},
"threatInfo": {
"threatTypes": ["MALWARE", "SOCIAL_ENGINEERING", "UNWANTED_SOFTWARE"],
"platformTypes": ["ANY_PLATFORM"],
"threatEntryTypes": ["URL"],
"threatEntries": [{"url": url}]
}
}
try:
response = requests.post(endpoint, json=payload, timeout=5)
response.raise_for_status()
data = response.json()
if "matches" in data:
return "Phishing"
return "Legitimate"
except Exception as e:
return "Legitimate"
def extract_features(url):
parsed = urlparse(url)
hostname = parsed.hostname if parsed.hostname else ''
path = parsed.path if parsed.path else ''
entropy = 0.0
if len(url) > 0:
for c in set(url):
p = url.count(c) / len(url)
entropy -= p * np.log2(p) if p > 0 else 0
features = {
"length_url": len(url),
"length_hostname": len(hostname),
"ip": 1 if any(char.isdigit() for char in hostname) else 0,
"nb_dots": url.count('.'),
"nb_hyphens": url.count('-'),
"nb_at": url.count('@'),
"nb_qm": url.count('?'),
"nb_and": url.count('&'),
"nb_or": url.count('|'),
"nb_eq": url.count('='),
"nb_underscore": url.count('_'),
"nb_tilde": url.count('~'),
"nb_percent": url.count('%'),
"nb_slash": url.count('/'),
"nb_star": url.count('*'),
"nb_colon": url.count(':'),
"nb_comma": url.count(','),
"nb_semicolumn": url.count(';'),
"nb_dollar": url.count('$'),
"nb_space": url.count(' '),
"nb_www": 1 if "www" in url else 0,
"nb_com": 1 if ".com" in url else 0,
"nb_dslash": url.count('//'),
"http_in_path": 1 if "http" in path else 0,
"https_token": 1 if "https" in url else 0,
"ratio_digits_url": sum(c.isdigit() for c in url) / len(url) if len(url) > 0 else 0,
"ratio_digits_host": sum(c.isdigit() for c in hostname) / len(hostname) if hostname else 0,
"punycode": 1 if re.search(r'xn--', url, re.IGNORECASE) else 0,
"port": parsed.port if parsed.port else 0,
"tld_in_path": 1 if any(tld in path for tld in ['.com', '.net', '.org', '.gov', '.edu']) else 0,
"tld_in_subdomain": 1 if any(tld in hostname for tld in ['.com', '.net', '.org', '.gov', '.edu']) else 0,
"abnormal_subdomain": 1 if len(hostname.split('.')) > 3 else 0,
"nb_subdomains": len(hostname.split('.')) - 1,
"prefix_suffix": 1 if url.startswith("www") else 0,
"shortening_service": 1 if any(short in url for short in ['bit.ly', 'goo.gl', 'tinyurl.com']) else 0,
"path_extension": 1 if any(ext in path for ext in ['.exe', '.zip', '.rar', '.tar', '.pdf']) else 0,
"length_words_raw": len(url.split()),
"char_repeat": len(set(url)),
"shortest_words_raw": min(len(word) for word in url.split()) if url.split() else 0,
"longest_words_raw": max(len(word) for word in url.split()) if url.split() else 0,
"shortest_word_host": min(len(word) for word in hostname.split('.')) if hostname else 0,
"longest_word_host": max(len(word) for word in hostname.split('.')) if hostname else 0,
"shortest_word_path": min(len(word) for word in path.split('/')) if path else 0,
"longest_word_path": max(len(word) for word in path.split('/')) if path else 0,
"avg_words_raw": np.mean([len(word) for word in url.split()]) if url.split() else 0,
"avg_word_host": np.mean([len(word) for word in hostname.split('.')]) if hostname else 0,
"avg_word_path": np.mean([len(word) for word in path.split('/')]) if path else 0,
"phish_hints": 1 if any(kw in url.lower() for kw in ['login', 'secure', 'verify', 'account']) else 0,
"domain_in_brand": 1 if 'apple' in hostname.lower() else 0,
"brand_in_subdomain": 1 if 'apple' in (hostname.split('.')[0] if hostname else '') else 0,
"brand_in_path": 1 if 'apple' in path.lower() else 0,
"suspicious_tld": 1 if hostname.endswith(('.xyz', '.top', '.club', '.gq', '.cf', '.tk')) else 0,
"entropy": entropy
}
return features
def predict_with_model(url):
try:
features = extract_features(url)
input_data = pd.DataFrame([features], columns=feature_names)
for col in non_numeric_columns:
if col in input_data.columns and col in label_encoders:
try:
input_data[col] = label_encoders[col].transform(input_data[col].astype(str))
except ValueError:
input_data[col] = len(label_encoders[col].classes_)
input_scaled = scaler.transform(input_data)
dmatrix = xgb.DMatrix(input_scaled)
phish_prob = model.predict(dmatrix)[0]
if phish_prob >= 0.95:
return "Phishing"
else:
return "Legitimate"
except Exception as e:
return "Legitimate"
def predict_url(url):
try:
if not url.startswith(("http://", "https://")):
url = "https://" + url
# Check VirusTotal
vt_result = check_url_virustotal(url)
if vt_result == "Phishing":
return "Phishing"
# Check PhishTank
pt_result = check_url_phis极
if pt_result == "Phishing":
return "Phishing"
# Check Google Safe Browsing
gsb_result = check_url_safebrowsing(url)
if gsb_result == "Phishing":
return "Phishing"
# If all three services return "Legitimate", return "Legitimate" without running the model
if vt_result == "Legitimate" and pt_result == "Legitimate" and gsb_result == "Legitimate":
return "Legitimate"
ml_result = predict_with_model(url)
return ml_result
except Exception as e:
return "Legitimate"
# Gradio Interface
interface = gr.Interface(
fn=predict_url,
inputs=gr.Textbox(label="Enter URL", placeholder="https://example.com"),
outputs=gr.Textbox(label="Result"),
title="Phishing URL Detector",
description="Check if a URL is phishing or legitimate",
examples=[
["https://www.apple.com"],
["https://login-facebook-secure.xyz/login.php"],
["https://bit.ly/suspicious-download"]
]
)
interface.launch(server_name="0.0.0.0", server_port=7860)