Bonjour, aujourd'hui, parlons programmation et sécurité.
Comme on le sait tous maintenant, le monde sur internet est régis par les mots de passe. Ces mots de passe sont souvent assez contraignant, et comment retenir plus d'une dizaine de mots de passe différents ? Et pourtant, on est bien obligé de les retenir, pour notre sécurité. Et concrètement, ça donne quoi ?Je vais réaliser différents code en langage C (ne vous prenez pas trop la tête à comprendre quoi fait quoi dans le code, je vais vous expliquer de manière générale ce que le code fait). Nous traiterons différents cas de figures : un mot de passe avec seulement des chiffres, un mot de passe avec seulement des caractères, un mot de passe avec différents caractères (chiffre, lettre, spécial, etc), et pour finir, un mot du dictionnaire.
C:
/*Importation des librairies*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
/*Définition du mot de passe*/
#define MAX_LENGTH 6
#define TARGET_PASS "123456"
/*Exécution*/
int main() {
/*Création de la chaine de caractères password*/
char password[MAX_LENGTH + 1];
/*Variable booléenne*/
int found = 0;
/*Initialisation des variables de temps*/
struct timeval start_time, end_time;
gettimeofday(&start_time, NULL);
/*Boucle de recherche du mot de passe*/
for (int i = 0; i <= 999999 && !found; ++i) {
snprintf(password, MAX_LENGTH + 1, "%06d", i);
if (strcmp(password, TARGET_PASS) == 0) {
found = 1;
printf("Mot de passe cracké : %s\n", password);
}
}
/*Calcul du temps écoulé et affichage*/
gettimeofday(&end_time, NULL);
double total_time = (end_time.tv_sec - start_time.tv_sec) + (double)(end_time.tv_usec - start_time.tv_usec) / 1000;
printf("Temps écoulé : %.4f millisecondes\n", total_time);
return 0;
}
Ce qu'on fait concrètement dans ce code, c'est qu'on initialise un mot de passe (
#define TARGET_PASS "123456"
) et on indique sa taille (#define MAX_LENGTH 6
), on initialise les différentes variables pour calculer la durée d'exécution, ainsi qu'une variable found
qui servira d'indicateur si le mot de passe a été trouvé ou non. Pour trouver le mot de passe, on utilise une boucle, qui parcourira toutes les valeurs entre 0 et 999999, on sortira de cette boucle si on a trouvé le mot de passe à l'aide de la variable found
. Puis, on affiche les résultats.Nous nous retrouvons avec :
Code:
$> ./password
Mot de passe cracké : 123456
Temps écoulé : 9.0780 millisecondes
$>
Cependant, le résultat (le temps), peut varier. Donc remanions le code pour générer des mots de passe aléatoirement, et tenter de les trouver. On affichera une moyenne à la fin de l'exécution.
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#define MAX_LENGTH 6
/* Fonction pour générer un mot de passe aléatoirement */
void generateRandomPassword(char *password) {
for (int i = 0; i < MAX_LENGTH; ++i) {
password = '0' + rand() % 10;
}
password[MAX_LENGTH] = '\0';
}
/* Exécution */
int main() {
char TARGET_PASS[MAX_LENGTH + 1];[I] // Allouer de la mémoire pour stocker le mot de passe cible[/I]
double somme = 0.0;
for (int k = 0; k < 10; k++) {
generateRandomPassword(TARGET_PASS); // Passer le tableau de caractères alloué au lieu d'un pointeur constant
char password[MAX_LENGTH + 1];
int found = 0;
struct timeval start_time, end_time;
gettimeofday(&start_time, NULL);
for (int i = 0; i <= 999999 && !found; ++i) {
snprintf(password, MAX_LENGTH + 1, "%06d", i);
if (strcmp(password, TARGET_PASS) == 0) {
found = 1;
printf("Mot de passe cracké : %s\n", password);
}
}
gettimeofday(&end_time, NULL);
double total_time = (end_time.tv_sec - start_time.tv_sec) + (double)(end_time.tv_usec - start_time.tv_usec) / 1000;
printf("Temps écoulé : %.4f millisecondes\n", total_time);
somme += total_time;
}
printf("\n");
printf("Temps moyen écoulé : %.4f millisecondes\n",somme/10);
return 0;
}
Ce qu'on vient concrètement de changer, c'est que nous avons ajouté une fonction pour générer un mot de passe aléatoirement, et maintenant, nous générons un mot de passe aléatoirement, on recherche le mot de passe, nous stockons le temps d'exécution dans une variable somme, et le tout 10 fois car nous sommes dans une boucle. A la fin, on affiche le résultat, donc la somme divisée par 10.
Nous tombons donc sur :
Code:
$> ./password
Mot de passe cracké : 367535
Temps écoulé : 19.6110 millisecondes
Mot de passe cracké : 629127
Temps écoulé : 27.7840 millisecondes
Mot de passe cracké : 093606
Temps écoulé : 4.3740 millisecondes
Mot de passe cracké : 261879
Temps écoulé : 11.6390 millisecondes
Mot de passe cracké : 202375
Temps écoulé : 9.4630 millisecondes
Mot de passe cracké : 922897
Temps écoulé : 43.5810 millisecondes
Mot de passe cracké : 361293
Temps écoulé : 15.9320 millisecondes
Mot de passe cracké : 194784
Temps écoulé : 8.7290 millisecondes
Mot de passe cracké : 503610
Temps écoulé : 22.1430 millisecondes
Mot de passe cracké : 632061
Temps écoulé : 28.3490 millisecondes
Temps moyen écoulé : 19.1605 millisecondes
$>
Donc seulement 19 millisecondes... Mais ça donne quoi avec des lettres aléatoires ? Car en théorie, on passe d'une combinaison entre 0 et 9, à A et Z (donc 26 possibilités car 26 lettres).
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MAX_LENGTH 6
/* Fonction pour générer un mot de passe aléatoirement */
void generateRandomPassword(char *password) {
for (int i = 0; i < MAX_LENGTH; ++i) {
password = 'a' + rand() % 26; // Chiffre aléatoire entre 'a' et 'z'
}
password[MAX_LENGTH] = '\0';
}[/SIZE]
/* Exécution */
[SIZE=4]int main() {
char TARGET_PASS[MAX_LENGTH + 1]; // Allouer de la mémoire pour stocker le mot de passe cible
long double somme = 0.0;
for (int k = 0; k < 10; k++) {
generateRandomPassword(TARGET_PASS); // Passer le tableau de caractères alloué au lieu d'un pointeur constant
char password[MAX_LENGTH + 1];
int found = 0;
struct timespec start_time, end_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
for (int i = 0; i <= 999999 && !found; ++i) {
generateRandomPassword(password); // Générer un nouveau mot de passe aléatoire
if (strcmp(password, TARGET_PASS) == 0) {
found = 1;
printf("Mot de passe cracké : %s\n", password);
}
}
clock_gettime(CLOCK_MONOTONIC, &end_time);
double total_time = (end_time.tv_sec - start_time.tv_sec) * 1000.0 + (double)(end_time.tv_nsec - start_time.tv_nsec) / 1000000.0;
if(total_time < 0){
total_time = -total_time;
}
printf("Temps écoulé : %.4f millisecondes\n", total_time);
somme += total_time;
}
printf("\n");
printf("Temps moyen écoulé : %.4Lf millisecondes\n", somme / 10);
return 0;
}
Nous avons :
Code:
$> ./password
Temps écoulé : 92.5627 millisecondes
Temps écoulé : 79.7321 millisecondes
Temps écoulé : 81.4394 millisecondes
Temps écoulé : 81.1473 millisecondes
Temps écoulé : 81.3780 millisecondes
Temps écoulé : 81.6272 millisecondes
Temps écoulé : 81.6914 millisecondes
Temps écoulé : 82.3126 millisecondes
Temps écoulé : 81.3545 millisecondes
Temps écoulé : 81.3483 millisecondes
Temps moyen écoulé : 82.4594 millisecondes
$>
Wow, 82 millisecondes. C'est drastiquement plus long en effet. Mais dans ce cas, que se passe-t-il si on prend un mot de passe avec différents caractères ?
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MAX_LENGTH 10
//Tous les caractères possibles
#define NUM_CHARS 62
const char *CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+{}[]|\\:;<>,.?/~`";
/* Fonction pour générer un mot de passe aléatoirement */
void generateRandomPassword(char *password) {
for (int i = 0; i < MAX_LENGTH; ++i) {
password = CHARS[rand() % NUM_CHARS]; // Choisir un caractère aléatoire de la liste CHARS
}
password[MAX_LENGTH] = '\0';
}
/* Exécution */
int main() {
char TARGET_PASS[MAX_LENGTH + 1]; // Allouer de la mémoire pour stocker le mot de passe cible
long double somme = 0.0;
for (int k = 0; k < 10; k++) {
generateRandomPassword(TARGET_PASS); // Passer le tableau de caractères alloué au lieu d'un pointeur constant
char password[MAX_LENGTH + 1];
int found = 0;
struct timespec start_time, end_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
for (int i = 0; i <= 999999 && !found; ++i) {
generateRandomPassword(password); // Générer un nouveau mot de passe aléatoire
if (strcmp(password, TARGET_PASS) == 0) {
found = 1;
printf("Mot de passe cracké : %s\n", password);
}
}
clock_gettime(CLOCK_MONOTONIC, &end_time);
double total_time = (end_time.tv_sec - start_time.tv_sec) * 1000.0 + (double)(end_time.tv_nsec - start_time.tv_nsec) / 1000000.0;
if(total_time < 0){
total_time = -total_time;
}
printf("Temps écoulé : %.4f millisecondes\n", total_time);
somme += total_time;
}
printf("\n");
printf("Temps moyen écoulé : %.4Lf millisecondes\n", somme / 10);
return 0;
}
Voici le résultat :
Code:
$> ./password
Temps écoulé : 130.9251 millisecondes
Temps écoulé : 127.2518 millisecondes
Temps écoulé : 127.1703 millisecondes
Temps écoulé : 138.8559 millisecondes
Temps écoulé : 127.6754 millisecondes
Temps écoulé : 127.3461 millisecondes
Temps écoulé : 127.1704 millisecondes
Temps écoulé : 127.2650 millisecondes
Temps écoulé : 127.6003 millisecondes
Temps écoulé : 127.3118 millisecondes
Temps moyen écoulé : 128.8572 millisecondes
$>
AH OUI ! C'est un grand changement. Cependant le problème, c'est que qui peut retenir une stupide suite de caractères aléatoires ? Nous, en tant que personne censée, on peut seulement retenir une suite de mot logique, comme un mot par exemple.
J'ai donc téléchargé un fichier (dico.txt) qui contient quelques centaines de mots (tirés du dictionnaire). La fonction pour générer un mot de passe vient donc piocher dans ce fichier pour trouver un mot, et pour le cracker, on vient aussi le parcourir.
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MAX_LENGTH 100 // Longueur maximale d'un mot dans le dictionnaire
// Fonction pour générer un mot de passe aléatoirement à partir du fichier "dico.txt"
void generateRandomPassword(char *password) {
// Ouvrir le fichier en mode lecture
FILE *file = fopen("dico.txt", "r");
if (file == NULL) {
printf("Erreur lors de l'ouverture du fichier dico.txt\n");
exit(1);
}
// Choisir un numéro de ligne aléatoire
int num_lines = 0;
char line[MAX_LENGTH];
while (fgets(line, MAX_LENGTH, file) != NULL) {
num_lines++;
}
rewind(file); // Rembobiner le fichier pour lire à partir du début
int random_line = rand() % num_lines;
// Lire et ignorer les lignes jusqu'à ce que nous atteignions la ligne aléatoire choisie
for (int i = 0; i < random_line; i++) {
fgets(line, MAX_LENGTH, file);
}
// Copier le mot dans le mot de passe
fgets(password, MAX_LENGTH, file);
// Supprimer le caractère de nouvelle ligne s'il est présent
char *newline_ptr = strchr(password, '\n');
if (newline_ptr != NULL) {
*newline_ptr = '\0';
}
// Fermer le fichier
fclose(file);
}
// Exécution
int main() {
char TARGET_PASS[MAX_LENGTH + 1]; // Allouer de la mémoire pour stocker le mot de passe cible
long double somme = 0.0;
for (int k = 0; k < 10; k++) {
generateRandomPassword(TARGET_PASS); // Générer un mot de passe à partir du fichier "dico.txt"
char password[MAX_LENGTH + 1];
int found = 0;
struct timespec start_time, end_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
// Ouvrir le fichier en mode lecture
FILE *file = fopen("dico.txt", "r");
if (file == NULL) {
printf("Erreur lors de l'ouverture du fichier dico.txt\n");
return 1;
}
// Parcourir le fichier ligne par ligne pour la recherche
char line[MAX_LENGTH];
while (fgets(line, MAX_LENGTH, file) != NULL && !found) {
// Supprimer le caractère de nouvelle ligne s'il est présent
char *newline_ptr = strchr(line, '\n');
if (newline_ptr != NULL) {
*newline_ptr = '\0';
}
// Comparer le mot de passe avec le mot de la ligne actuelle
if (strcmp(line, TARGET_PASS) == 0) {
found = 1;
printf("Mot de passe cracké : %s\n", line);
}
}
// Fermer le fichier
fclose(file);
clock_gettime(CLOCK_MONOTONIC, &end_time);
double total_time = (end_time.tv_sec - start_time.tv_sec) * 1000.0 + (double)(end_time.tv_nsec - start_time.tv_nsec) / 1000000.0;
if(total_time < 0){
total_time = -total_time;
}
printf("Temps écoulé : %.4f millisecondes\n", total_time);
somme += total_time;
}
printf("\n");
printf("Temps moyen écoulé : %.4Lf millisecondes\n", somme / 10);
return 0;
}
Ce qui donne :
Code:
$> ./password
Mot de passe cracké : fonce
Temps écoulé : 0.0843 millisecondes
Mot de passe cracké : tirer
Temps écoulé : 0.0551 millisecondes
Mot de passe cracké : coquin
Temps écoulé : 0.0405 millisecondes
Mot de passe cracké : different
Temps écoulé : 0.0454 millisecondes
Mot de passe cracké : couleur
Temps écoulé : 0.0233 millisecondes
Mot de passe cracké : tunnel
Temps écoulé : 0.0896 millisecondes
Mot de passe cracké : ligne
Temps écoulé : 0.0285 millisecondes
Mot de passe cracké : apporter
Temps écoulé : 0.0179 millisecondes
Mot de passe cracké : appareil
Temps écoulé : 0.0287 millisecondes
Mot de passe cracké : couper
Temps écoulé : 0.0436 millisecondes
Temps moyen écoulé : 0.0457 millisecondes
$>
C'est... affligeant. En effet, c'est pas bien long de venir trouver un simple mot dans un dictionnaire, et c'est pas pour rien que les pires mots de passe sont ceux tirés d'un dictionnaire. Cependant, tentons un truc. Essayons de mettre un temps d'attente d'une seconde entre chaque recherche.
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define MAX_LENGTH 100 // Longueur maximale d'un mot dans le dictionnaire
// Fonction pour générer un mot de passe aléatoirement à partir du fichier "dico.txt"
void generateRandomPassword(char *password) {
// Ouvrir le fichier en mode lecture
FILE *file = fopen("dico.txt", "r");
if (file == NULL) {
printf("Erreur lors de l'ouverture du fichier dico.txt\n");
exit(1);
}
// Choisir un numéro de ligne aléatoire
int num_lines = 0;
char line[MAX_LENGTH];
while (fgets(line, MAX_LENGTH, file) != NULL) {
num_lines++;
}
rewind(file); // Rembobiner le fichier pour lire à partir du début
int random_line = rand() % num_lines;
// Lire et ignorer les lignes jusqu'à ce que nous atteignions la ligne aléatoire choisie
for (int i = 0; i < random_line; i++) {
fgets(line, MAX_LENGTH, file);
}
// Copier le mot dans le mot de passe
fgets(password, MAX_LENGTH, file);
// Supprimer le caractère de nouvelle ligne s'il est présent
char *newline_ptr = strchr(password, '\n');
if (newline_ptr != NULL) {
*newline_ptr = '\0';
}
// Fermer le fichier[
fclose(file);
}
// Exécution
int main() {
char TARGET_PASS[MAX_LENGTH + 1]; // Allouer de la mémoire pour stocker le mot de passe cible
generateRandomPassword(TARGET_PASS); // Générer un mot de passe à partir du fichier "dico.txt"
printf("Mot de passe à rechercher : %s\n", TARGET_PASS);
struct timespec start_time, end_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
// Ouverture du fichier en mode lecture
FILE *file = fopen("dico.txt", "r");
if (file == NULL) {
printf("Erreur lors de l'ouverture du fichier dico.txt\n");
return 1;
}
// Parcourir le fichier ligne par ligne pour la recherche
char line[MAX_LENGTH];
int found = 0;
while (fgets(line, MAX_LENGTH, file) != NULL && !found) {
// Supprimer le caractère de nouvelle s'il est présent[/I]
char *newline_ptr = strchr(line, '\n');
if (newline_ptr != NULL) {
*newline_ptr = '\0';
}
// Comparer le mot de passe avec le mot de la ligne actuelle
if (strcmp(line, TARGET_PASS) == 0) {
printf("Mot de passe cracké : %s\n", line);
found = 1;
break;
}
// Attente d'une seconde
usleep(1000000);
}
// Fermer le fichier
fclose(file);
clock_gettime(CLOCK_MONOTONIC, &end_time);
double total_time = (end_time.tv_sec - start_time.tv_sec) + (double)(end_time.tv_nsec - start_time.tv_nsec) / 1000000000.0;
printf("Temps écoulé : %.4f secondes\n", total_time);
return 0;
}
Donc :
Code:
$> ./password
Mot de passe à rechercher : fonce
Mot de passe cracké : fonce
Temps écoulé : 559.7000 secondes
$>
C'est beaucoup non ? Ca fait environ 8 minutes. Et je rappelle que mettre un mot d'un dictionnaire c'est très peu sécurisé. Donc on a transformé les pires des mots de passe, en mots de passe passables. Et c'est pour cela que lorsque vous entrez un mauvais mot de passe, ça met du temps avant de vous dire que votre mot de passe est incorrect lors de votre connexion, car y'a ce petit délai (couplé avec des sécurités supplémentaires évidemment). On aurait cependant pu aller plus vite en faisant une recherche dichotomique, et en utilisant une machine plus puissante pour faire les calculs.
Mais enfin, pas la peine de vous casser la tête à faire une suite aléatoire de caractères pour vous construire un bon mot de passe. Vous pouvez choisir le refrain d'une musique que vous aimez, ne garder que les premières lettres de chaque mots (en conservant les majuscules), remplacer certains caractères par d'autres (un O par un 0, un a par un @ ou 4, etc). Vous aurez donc un mot de passe assez fort, que vous oublierez moins facilement.
Sans oublier que la taille compte (malheureusement pour vous messieurs), et pour l'expliquer, rien de mieux que les mathématiques. Reprenons notre mot de passe basique avec les nombres, un mot de passe de 4 chiffres aura 10⁴ possibilités, donc 10 000 possibilités. Mais changeons la taille, et prenons 6 chiffres, on passe donc à 10⁶, donc 1 000 000 de possibilités, ce qui est énormément plus. Et si on prend l'exemple des lettres aléatoires, avec 4 lettres, on a 26⁴ possibilités, donc 456 976 possibilités. Et avec 6 lettres, ça fait 308 915 776 possibilités, ce qui est énorme.
Post scriptum : Lors des tests avec les combinaisons de lettres, les combinaisons sont SOIT en minuscules, SOIT en majuscule, et non un mix des deux. Si nous avions mélangés les deux, nous aurions eu une complexité deux fois plus grande.
- Description courte
- Comment un mot de passe est-il considéré complexe ? Et pourquoi faut-il choisir un bon mot de passe ?
Dernière édition: