Pourquoi vous ne devriez pas exposer vos auto-incréments

Par défaut les schémas de base de données utilisent des auto-incréments pour leurs ressources. Voici comment éviter d'exposer ces informations sensibles.

Jérémy 🤘
Jérémy 🤘

Quand vous créer une table dans une base de données, il vous est proposé quelque chose comme ça id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY. Mais exposer ce nombre peut justement créer des failles de sécurité dans votre application.

Failles

Sécurité des données

Quand vous exposez ce nombre dans une URL, il est assez facile de savoir ce qui précède et ce qui suit. Par exemple si vous avez une URL comme ceci https://www.myapp.com/users/10. Il est assez facile de savoir que l'utilisateur précédent sera le 9 et le suivant le 11.

Si 10 est votre identifiant de base de données et que vous pouvez via cette URL voir vos informations personnelles, si vous allez sur https://www.myapp.com/users/9 vous devriez avoir une erreur 403 Forbidden car ce n'est pas vous. Mais si vous oubliez cette vérification vous avez un gros risque pour la sécurité de votre site.

Récolte des ressources

Par la même occasion, si vous stockez des images sur votre site comme ceci https://www.myapp.com/images/avatar-10.jpg. Rien de vous empêche d'aller regarder l'image avatar-9.jpg ou avatar-11.jpg. Vous allez donc potentiellement récupérer des images qui ne vous appartiennent pas. C'est ce que l'on appelle la Récolte de ressources ou en anglais Resource harvesting.

Imaginons que sur cette même URL vous rajoutiez un paramètre ?width= qui permet de redimensionner automatiquement l'image et si dans cette nouvelle taille n'existe pas, vous l'enresitrez sur votre serveur. Rien ne n'empêche de faire des millers voire des millions d'appels pour aller prendre toutes vos images et les redimensionner pixel par pixel. Vous aurez alors un serveur plein en quelques minutes.

CSRF

CSRF, pour Cross-Site Request Forgery, est une faille de sécurité qui consiste à exécuter un code à la place d'un autre utilisateur. Par exemple, vous avez cette URL https://www.myapp.com/users/10/delete qui permet de supprimer l'utilisateur quand il envoie une requête POST dessus. Vous avez fait en sorte de protégé l'accès pour que ce soit bien l'utilisateur 10 qui puisse accéder à cette page. Mais par exemple vous n'avez pas protégé le fait que même avec une requête GET il puisse s'y rendre.

Si sur le site je peux poster des commentaires et que le HTML n'est pas échapé, dans mon code je peux mettre une image <img src="https://www.myapp.com/users/10/delete">. Quand l'utilisateur va se rendre sur la page, le navigateur va charger cette fausse image et le compte sera supprimé.

Solutions

Heureusement il existe des solutions pour se prémunir de ces problèmes.

Slugs

Pour rappel un slug est le fait de remplacer tous les caractères spéciaux en leur équivalant en caractère normal et les espaces par des tirets. Par exemple Un article sur la sécurité devient un-article-sur-la-securite. C'est notamment ce qui est fait sur mon blog. Vous n'avez pas connaissance des identifiants des mes articles pourtant ils existent bien en base de données et vous pouvez lire les articles grâce aux slugs.

Ils sont de bons moyens de ne pas afficher ces nombres. Si vous rajoutez une contrainte dans votre base de données, ils peuvent eux aussi devenir uniques et ainsi vous n'exposez rien de sensible dans votre base de données. Mais attention toutefois, ils ne conviennent pas dans tous les cas, par exemple pour afficher les informations d'une commande ou autre cela n'est pas pertinent.

UUID

UUID, pour Universally Unique Identifier est un identifiant unique qui peut être basé sur plusieurs critères pour le calculer permet d'avoir une chaîne de caractères arbitraires qui est totalement différente de la précedente mais aussi de la suivante. Voici un exemple de UUID 92f446c3-9d04-42d1-bc5d-7249e017e6ea.

Il est donc très compliqué d'aller récupérer la commande précédente ou suivante quand vous avez une URL comme cela https://www.myapp.com/orders/0fda9802-cca8-4366-ad55-c61c6860fff7.

Côté programmation, en PHP il existe bon nombre de générateur de UUID, le plus rependu est sans doute ramsey/uuid et qui existe aussi sous forme d'extension Doctrine ramsey/uuid-doctrine.

Source