suite des maj
This commit is contained in:
@ -735,3 +735,97 @@ async def get_legitimate_ja4(
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Erreur: {str(e)}")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# ENDPOINT — Corrélation JA4 × ASN / Pays (C5)
|
||||
# Détecte les JA4 fortement concentrés sur un seul ASN ou pays
|
||||
# → signal de botnet ciblé ou d'infrastructure de test/attaque partagée
|
||||
# =============================================================================
|
||||
|
||||
@router.get("/asn-correlation")
|
||||
async def get_ja4_asn_correlation(
|
||||
min_concentration: float = Query(0.7, ge=0.0, le=1.0, description="Seuil min de concentration ASN ou pays"),
|
||||
min_ips: int = Query(5, ge=1, description="Nombre minimum d'IPs par JA4"),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
):
|
||||
"""
|
||||
Identifie les JA4 fingerprints fortement concentrés sur un seul ASN ou pays.
|
||||
Un JA4 avec asn_concentration ≥ 0.7 signifie que ≥70% des IPs utilisant ce fingerprint
|
||||
proviennent du même ASN → infrastructure de bot partagée ou datacenter suspect.
|
||||
"""
|
||||
try:
|
||||
# Two-pass: first aggregate per (ja4, asn) to get IP counts per ASN,
|
||||
# then aggregate per ja4 to compute concentration ratio
|
||||
sql = """
|
||||
SELECT
|
||||
ja4,
|
||||
sum(ips_per_combo) AS unique_ips,
|
||||
uniq(src_asn) AS unique_asns,
|
||||
uniq(src_country_code) AS unique_countries,
|
||||
toString(argMax(src_asn, ips_per_combo)) AS top_asn_number,
|
||||
argMax(asn_name, ips_per_combo) AS top_asn_name,
|
||||
argMax(src_country_code, country_ips) AS dominant_country,
|
||||
sum(total_hits) AS total_hits,
|
||||
round(max(ips_per_combo) / greatest(sum(ips_per_combo), 1), 3) AS asn_concentration,
|
||||
round(max(country_ips) / greatest(sum(ips_per_combo), 1), 3) AS country_concentration
|
||||
FROM (
|
||||
SELECT
|
||||
ja4,
|
||||
src_asn,
|
||||
src_country_code,
|
||||
any(src_as_name) AS asn_name,
|
||||
uniq(src_ip) AS ips_per_combo,
|
||||
uniq(src_ip) AS country_ips,
|
||||
sum(hits) AS total_hits
|
||||
FROM mabase_prod.agg_host_ip_ja4_1h
|
||||
WHERE window_start >= now() - INTERVAL 24 HOUR
|
||||
AND ja4 != ''
|
||||
GROUP BY ja4, src_asn, src_country_code
|
||||
)
|
||||
GROUP BY ja4
|
||||
HAVING unique_ips >= %(min_ips)s
|
||||
AND (asn_concentration >= %(min_conc)s OR country_concentration >= %(min_conc)s)
|
||||
ORDER BY asn_concentration DESC, unique_ips DESC
|
||||
LIMIT %(limit)s
|
||||
"""
|
||||
result = db.query(sql, {"min_ips": min_ips, "min_conc": min_concentration, "limit": limit})
|
||||
items = []
|
||||
for row in result.result_rows:
|
||||
ja4 = str(row[0])
|
||||
unique_ips = int(row[1])
|
||||
unique_asns = int(row[2])
|
||||
unique_countries = int(row[3])
|
||||
top_asn_number = str(row[4] or "")
|
||||
top_asn_name = str(row[5] or "")
|
||||
dominant_country = str(row[6] or "")
|
||||
total_hits = int(row[7] or 0)
|
||||
asn_concentration = float(row[8] or 0)
|
||||
country_concentration = float(row[9] or 0)
|
||||
|
||||
if asn_concentration >= 0.85:
|
||||
corr_type, risk = "asn_monopoly", "high"
|
||||
elif asn_concentration >= min_concentration:
|
||||
corr_type, risk = "asn_dominant", "medium"
|
||||
elif country_concentration >= min_concentration:
|
||||
corr_type, risk = "geo_targeted", "medium"
|
||||
else:
|
||||
corr_type, risk = "distributed", "low"
|
||||
|
||||
items.append({
|
||||
"ja4": ja4,
|
||||
"unique_ips": unique_ips,
|
||||
"unique_asns": unique_asns,
|
||||
"unique_countries": unique_countries,
|
||||
"top_asn_name": top_asn_name,
|
||||
"top_asn_number": top_asn_number,
|
||||
"dominant_country": dominant_country,
|
||||
"total_hits": total_hits,
|
||||
"asn_concentration": asn_concentration,
|
||||
"country_concentration":country_concentration,
|
||||
"correlation_type": corr_type,
|
||||
"risk": risk,
|
||||
})
|
||||
return {"items": items, "total": len(items)}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Erreur: {str(e)}")
|
||||
|
||||
Reference in New Issue
Block a user