1.0.9 — imitation d'empreinte TLS en seconde tentative
This commit is contained in:
1 parent
12973dab5d
commit
d215b32a89
11 files changed
+86
-18
No files matched your search
@@ -2,6 +2,20 @@
|
||||
|
||||
Format inspiré de [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/).
|
||||
|
||||
## [1.0.9] — 2026-09-13
|
||||
|
||||
### Ajouté
|
||||
- **Imitation d'empreinte TLS sur la seconde tentative.** Les protections
|
||||
modernes identifient un client par son empreinte TLS et HTTP/2 avant même de
|
||||
lire ses en-têtes — c'est pourquoi les en-têtes seuls n'avaient récupéré que
|
||||
3 sites sur 506. Maigret embarque `CurlCffiChecker`, qui imite l'empreinte
|
||||
d'un vrai navigateur, mais ne l'active que pour les 108 sites portant
|
||||
`tls_fingerprint` en amont. Limier l'étend désormais à tous les sites rejoués
|
||||
en seconde passe. Désactivable par `LIMIER_TLS_IMPERSONATION_RETRY=false`.
|
||||
|
||||
Plus lent qu'aiohttp, donc réservé aux sites ayant déjà refusé la première
|
||||
requête — le coût ne porte que là où il peut rapporter.
|
||||
|
||||
## [1.0.8] — 2026-09-12
|
||||
|
||||
### Modifié
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "limier"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
description = "Plateforme OSINT auto-hébergée : pseudonyme, nom, e-mail, domaine"
|
||||
requires-python = ">=3.12,<3.14"
|
||||
license = { text = "MIT" }
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""Limier — plateforme OSINT auto-hébergée."""
|
||||
|
||||
__version__ = "1.0.8"
|
||||
__version__ = "1.0.9"
|
||||
@@ -127,6 +127,15 @@ class Settings(BaseSettings):
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0"
|
||||
)
|
||||
|
||||
tls_impersonation_retry: bool = True
|
||||
"""Imiter l'empreinte TLS d'un navigateur lors de la seconde tentative.
|
||||
|
||||
S'appuie sur ``curl_cffi``, déjà embarqué par Maigret. C'est le levier qui
|
||||
agit sur les protections modernes : elles identifient un client par son
|
||||
empreinte TLS bien avant de lire ses en-têtes. Plus lent qu'aiohttp, donc
|
||||
réservé aux sites ayant déjà refusé la première requête.
|
||||
"""
|
||||
|
||||
outbound_browser_headers: bool = True
|
||||
"""Présenter un jeu d'en-têtes de navigateur sur les requêtes sortantes.
|
||||
|
||||
|
||||
@@ -173,8 +173,30 @@ def selectionner_sites(
|
||||
)
|
||||
|
||||
|
||||
def dupliquer_avec_entetes(sites: dict) -> dict:
|
||||
"""Copie des sites donnés, avec les en-têtes de navigateur ajoutés.
|
||||
def dupliquer_pour_seconde_passe(sites: dict) -> dict:
|
||||
"""Copie des sites donnés, préparée pour une seconde tentative renforcée.
|
||||
|
||||
Deux renforts sont appliqués, du moins au plus efficace :
|
||||
|
||||
**Les en-têtes de navigateur**, pour les sites qui n'en déclarent pas. Gain
|
||||
mesuré en conditions réelles : 3 sites récupérés sur 506. Faible, mais réel.
|
||||
|
||||
**L'imitation d'empreinte TLS**, qui est le vrai levier. Les protections
|
||||
modernes identifient un client par son empreinte TLS et HTTP/2 avant même
|
||||
de lire ses en-têtes : annoncer Firefox tout en présentant l'empreinte de
|
||||
Python est précisément ce qui déclenche le blocage. Maigret embarque
|
||||
``CurlCffiChecker``, qui imite l'empreinte d'un vrai navigateur, mais ne
|
||||
l'active que pour les sites portant ``tls_fingerprint`` dans leur champ
|
||||
``protection`` — 108 sites sur 3 302 en amont. On l'étend ici à tous les
|
||||
sites de la seconde passe.
|
||||
|
||||
Ce renfort n'est pas gratuit : curl_cffi est plus lent qu'aiohttp et ouvre
|
||||
ses propres connexions. Le réserver aux sites ayant déjà échoué limite ce
|
||||
coût aux seuls cas où il peut rapporter quelque chose.
|
||||
|
||||
Note : pour ces sites, curl_cffi retire le User-Agent afin d'utiliser celui
|
||||
du navigateur imité — c'est voulu, la cohérence entre empreinte et en-têtes
|
||||
est justement ce qu'on cherche.
|
||||
|
||||
**Duplication et non modification sur place.** Les objets rendus par
|
||||
``ranked_sites_dict`` proviennent de la base en cache, partagée par tous les
|
||||
@@ -186,12 +208,26 @@ def dupliquer_avec_entetes(sites: dict) -> dict:
|
||||
"""
|
||||
from maigret.sites import MaigretSite
|
||||
|
||||
from limier.config import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
entetes = entetes_navigateur()
|
||||
copies = {}
|
||||
|
||||
for nom, site in sites.items():
|
||||
copie = MaigretSite(site.name, dict(site.json))
|
||||
copie.headers = dict(site.headers) if site.headers else dict(entetes)
|
||||
|
||||
if settings.outbound_browser_headers:
|
||||
copie.headers = dict(site.headers) if site.headers else dict(entetes)
|
||||
|
||||
if settings.tls_impersonation_retry:
|
||||
protection = list(site.protection or [])
|
||||
if "tls_fingerprint" not in protection:
|
||||
protection.append("tls_fingerprint")
|
||||
copie.protection = protection
|
||||
|
||||
copies[nom] = copie
|
||||
|
||||
return copies
|
||||
|
||||
|
||||
|
||||
@@ -347,8 +347,12 @@ class MoteurPseudonyme(Engine):
|
||||
module=self.name,
|
||||
level="info",
|
||||
message=(
|
||||
f"Seconde tentative sur {len(a_rejouer)} site(s) en échec, "
|
||||
"avec des en-têtes de navigateur."
|
||||
f"Seconde tentative sur {len(a_rejouer)} site(s) en échec"
|
||||
+ (
|
||||
", avec imitation d'empreinte TLS."
|
||||
if self.settings.tls_impersonation_retry
|
||||
else ", avec des en-têtes de navigateur."
|
||||
)
|
||||
),
|
||||
)
|
||||
# Le total inclut désormais cette seconde passe, sinon la barre de
|
||||
@@ -357,7 +361,7 @@ class MoteurPseudonyme(Engine):
|
||||
recuperes_avant = len(deja_vus)
|
||||
async for evenement in self._chercher_un(
|
||||
pseudo=pseudo,
|
||||
sites=database.dupliquer_avec_entetes(a_rejouer),
|
||||
sites=database.dupliquer_pour_seconde_passe(a_rejouer),
|
||||
compteur=compteur,
|
||||
label=f"{label} — seconde tentative",
|
||||
deja_vus=deja_vus,
|
||||
|
||||
+6
-1
@@ -7,7 +7,7 @@
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
REGISTRY=registry.tips-of-mine.com
|
||||
TAG=1.0.8
|
||||
TAG=1.0.9
|
||||
LIMIER_HOST=limier.tips-of-mine.com
|
||||
LIMIER_PUBLIC_URL=https://limier.tips-of-mine.com
|
||||
LIMIER_ENVIRONMENT=prod
|
||||
@@ -76,4 +76,9 @@ LIMIER_DNS_RESOLVERS=1.1.1.1,9.9.9.9
|
||||
# refusé la première requête. Mettre à false pour s'en tenir au comportement
|
||||
# natif de Maigret et comparer les taux de blocage.
|
||||
LIMIER_OUTBOUND_BROWSER_HEADERS=true
|
||||
|
||||
# Imitation d'empreinte TLS sur la seconde tentative (curl_cffi). C'est le
|
||||
# levier qui agit réellement sur les protections anti-robot. Mettre à false
|
||||
# pour mesurer ce qu'il apporte.
|
||||
LIMIER_TLS_IMPERSONATION_RETRY=true
|
||||
LIMIER_WORKER_MAX_JOBS=4
|
||||
@@ -56,7 +56,7 @@ x-limier-env: &limier-env
|
||||
services:
|
||||
|
||||
limier-api:
|
||||
image: ${REGISTRY:-registry.tips-of-mine.com}/limier/limier-backend:${TAG:-1.0.8}
|
||||
image: ${REGISTRY:-registry.tips-of-mine.com}/limier/limier-backend:${TAG:-1.0.9}
|
||||
container_name: limier-api
|
||||
restart: unless-stopped
|
||||
command: ["limier-api"]
|
||||
@@ -99,7 +99,7 @@ services:
|
||||
- "homepage.description=Plateforme OSINT"
|
||||
|
||||
limier-worker:
|
||||
image: ${REGISTRY:-registry.tips-of-mine.com}/limier/limier-backend:${TAG:-1.0.8}
|
||||
image: ${REGISTRY:-registry.tips-of-mine.com}/limier/limier-backend:${TAG:-1.0.9}
|
||||
container_name: limier-worker
|
||||
restart: unless-stopped
|
||||
command: ["limier-worker"]
|
||||
@@ -131,7 +131,7 @@ services:
|
||||
- "com.centurylinklabs.watchtower.enable=true"
|
||||
|
||||
limier-web:
|
||||
image: ${REGISTRY:-registry.tips-of-mine.com}/limier/limier-frontend:${TAG:-1.0.8}
|
||||
image: ${REGISTRY:-registry.tips-of-mine.com}/limier/limier-frontend:${TAG:-1.0.9}
|
||||
container_name: limier-web
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
@@ -214,7 +214,7 @@ services:
|
||||
# ``--profile``, pour que l'installation par défaut reste minimale.
|
||||
|
||||
limier-holehe:
|
||||
image: ${REGISTRY:-registry.tips-of-mine.com}/limier/limier-holehe:${TAG:-1.0.8}
|
||||
image: ${REGISTRY:-registry.tips-of-mine.com}/limier/limier-holehe:${TAG:-1.0.9}
|
||||
container_name: limier-holehe
|
||||
restart: unless-stopped
|
||||
profiles: ["holehe"]
|
||||
|
||||
@@ -12,7 +12,7 @@ Ni base de données, ni Redis, ni Authentik. On appelle le moteur directement.
|
||||
cd /opt # ou n'importe où, il suffit d'avoir essai_moteur.py sous la main
|
||||
|
||||
docker run --rm -v "$PWD/essai_moteur.py:/tmp/essai.py" \
|
||||
registry.tips-of-mine.com/limier/limier-backend:1.0.8 \
|
||||
registry.tips-of-mine.com/limier/limier-backend:1.0.9 \
|
||||
python /tmp/essai.py domain tips-of-mine.com
|
||||
```
|
||||
|
||||
@@ -24,7 +24,7 @@ Le moteur pseudonyme, en version courte :
|
||||
```bash
|
||||
docker run --rm -v "$PWD/essai_moteur.py:/tmp/essai.py" \
|
||||
-e LIMIER_USERNAME_TOP_SITES=80 \
|
||||
registry.tips-of-mine.com/limier/limier-backend:1.0.8 \
|
||||
registry.tips-of-mine.com/limier/limier-backend:1.0.9 \
|
||||
python /tmp/essai.py username soxoj
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ Et la recherche par nom, qui montre les variantes déduites :
|
||||
```bash
|
||||
docker run --rm -v "$PWD/essai_moteur.py:/tmp/essai.py" \
|
||||
-e LIMIER_USERNAME_TOP_SITES=60 \
|
||||
registry.tips-of-mine.com/limier/limier-backend:1.0.8 \
|
||||
registry.tips-of-mine.com/limier/limier-backend:1.0.9 \
|
||||
python /tmp/essai.py name "Hubert Cornet"
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "limier-frontend",
|
||||
"version": "1.0.8",
|
||||
"version": "1.0.9",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Reference in new issue
Block a user