# Des codes de billets séquentiels permettaient de deviner un billet valide sans payer

> Sur une billetterie événementielle, des identifiants de billets attribués de façon séquentielle rendaient possible la devinette d'un billet valide non payé. Analyse du problème et correction.

- Auteur : WordPress Développement
- Publié le : 2022-03-20
- Mis à jour le : 2022-03-20
- Catégorie : Sécurité
- URL : https://www.wpmoderne.fr/securite/deviner-code-billet-non-paye-billetterie/

## L’essentiel

- Un identifiant séquentiel se devine en quelques essais
- Un identifiant aléatoire ne suffit pas seul, il faut aussi vérifier le paiement
- La correction combine deux mesures indépendantes

Six chiffres. C'est tout ce qu'il fallait connaître pour accéder au billet électronique de n'importe quel participant à un salon professionnel, organisé par une structure événementielle qui gérait sa billetterie via une extension WordPress dédiée. Les billets étaient identifiés par un numéro incrémental simple, affiché en clair dans l'URL de la page de consultation du billet : `/mon-billet/000482/`, puis `000483`, puis `000484`, dans l'ordre exact d'achat.

Le paiement de la billetterie proprement dit, son intégration avec le prestataire de paiement, ne fait pas l'objet de cet article : le problème identifié ici se situait en aval, dans la façon dont un billet, une fois généré, devenait accessible. Un visiteur malintentionné n'avait même pas besoin de connaître un numéro précis : il suffisait d'incrémenter ou de décrémenter le chiffre dans l'URL d'un billet qu'il possédait déjà légitimement pour tomber, avec une probabilité élevée, sur le billet d'un autre participant.

## Ce que cette page révélait, une fois consultée

La page de consultation d'un billet affichait, sans aucune vérification d'identité du visiteur, le nom du participant, son entreprise, son statut d'accès (visiteur, exposant, VIP), et un QR code scannable à l'entrée de l'événement. En clair, connaître ou deviner un seul numéro de billet suffisait à obtenir tout ce qu'il fallait pour se faire passer pour ce participant à l'entrée, sans avoir jamais payé sa propre place.

## Pourquoi un identifiant séquentiel est une invitation à la devinette

> L'essentiel à retenir : Un identifiant séquentiel se devine en quelques essais ; Un identifiant aléatoire ne suffit pas seul, il faut aussi vérifier le paiement ; La correction combine deux mesures indépendantes

Un identifiant qui s'incrémente de un en un ne protège absolument rien : il ne fait que masquer, temporairement et faiblement, la structure des données. Dès lors qu'une seule valeur valide est connue (celle de son propre billet, par exemple), l'ensemble de la plage devient trivialement accessible par simple itération :

```
for i in $(seq 1 999999); do
  curl -s "https://salon-exemple.fr/mon-billet/$(printf '%06d' $i)/" \
    -o "billet_$i.html"
done
```

Ce script, volontairement simple, aurait permis de récupérer l'ensemble des billets émis en quelques heures, sans qu'aucune protection ne vienne le ralentir.

## La correction en deux temps

### Premier temps : remplacer l'identifiant séquentiel par un jeton non devinable

```
$jeton_billet = bin2hex( random_bytes( 16 ) );
update_post_meta( $billet_id, 'jeton_acces', $jeton_billet );
```

Le billet devient accessible via `/mon-billet/8f3a1c9e2b7d4f60.../`, un jeton de trente-deux caractères hexadécimaux impossible à deviner par simple itération. Cette mesure seule referme la porte de la devinette en masse.

### Second temps : vérifier que le jeton correspond bien à un billet payé

Un jeton non devinable ne suffit pas si un attaquant parvient malgré tout, par un autre biais (un lien partagé publiquement sur un réseau social par erreur, par exemple), à obtenir un jeton valide correspondant à un billet jamais réglé. La page de consultation vérifie donc systématiquement, en plus de la validité du jeton, que le statut de paiement associé au billet est bien confirmé :

```
if ( ! $jeton_valide || get_post_meta( $billet_id, 'statut_paiement', true ) !== 'confirme' ) {
    wp_die( 'Billet introuvable ou non valide.' );
}
```

## Un identifiant non devinable n'est pas une protection suffisante à lui seul

Il faut résister à la tentation de considérer qu'un jeton aléatoire suffit à sécuriser une ressource sensible. C'est un principe utile, connu sous le nom de sécurité par le nombre (par opposition à la sécurité par l'obscurité pure), mais il doit toujours se combiner à une vérification métier réelle : ici, la confirmation effective du paiement, indépendamment de la difficulté à deviner l'identifiant.

> Rendre un identifiant impossible à deviner referme une porte ; vérifier ce que cet identifiant est censé représenter en referme une autre. Les deux sont nécessaires, ni l'une ni l'autre ne suffit seule.

## En résumé

Des identifiants séquentiels sur une ressource sensible, ici un billet donnant accès physique à un événement, transforment n'importe quel billet légitime en clé d'exploration de l'ensemble du système. La correction, un jeton aléatoire combiné à une vérification de statut de paiement, aurait dû faire partie de la conception initiale plutôt que d'un correctif tardif après incident.
