OVH Cloud OVH Cloud

Problème C++

106 réponses
Avatar
Steph
boujour

J'ai créé un programme dont voici une partie :


#include <iostream>
using namespace std;

int main (void)

{
char pw[20];
const char valide[20]="12H12";

cout << " Password :";
cin >> pw;

while (strcmp(pw,valide) = =1)

{

cout << " ACCESS REFUSED\n ";
cout << " Password :";
cin >> pw;

}


cout << "\n ACCESS GARANTED ";
}

Le problème est que si j'entre seulement le premiere lettre ou chiffre il
accepte alors que celles qui suivent sont fausses. Comment je peux faire
pour que le programme teste un à un les caratères?

merci

10 réponses

1 2 3 4 5
Avatar
Jean-Marc Bourguet
"kanze" writes:

J'avoue ne pas comprendre ces histoires de « balancée » et
d'autres.


Il se focalise sur une technique relativement difficile a mettre en
oeuvre et oublie les problemes qui me semblent plus simples et plus
importants (il y a moyen d'avoir des depassements de tampons, le mot
de passe est stocke en clair).

La methode en question consiste a mesurer le temps pris pour verifier
le mot de passe. Comme on arrete la boucle des qu'un caractere n'est
pas bon, en mesurant le temps on sait le nombre de caracteres bons en
tete qu'on a.

Cette methode a ete utilisee au moins une fois en pratique en mettant
le mot sur deux pages et en s'arrangeant pour que la deuxieme page
soit swappee out. La difference de temps est alors fort facilement
mesurable si le code faisant la verification n'y accede que quand
c'est necessaire.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Sylvain
Jean-Marc Bourguet wrote on 22/06/2006 09:16:

Les attaques par depassement de buffer sont d'un ordre de grandeur ou
deux plus courantes que les attaques temporelles. [...]


oui et ? ai-je seulement insinué que Cyrille avait tort de sécuriser la
saisie par un "cin >> std::string" ? il me semble avoir dit le contraire
plutot.
cette première étape, indispensable, ayant été indiquée et réalisée par
les posts précédent le mien, je n'ai parlé que des étapes suivantes.

Sylvain.

Avatar
Sylvain
Jean-Marc Bourguet wrote on 22/06/2006 11:41:
"kanze" writes:

Il se focalise sur une technique relativement difficile a mettre en
oeuvre et oublie les problemes qui me semblent plus simples et plus
importants (il y a moyen d'avoir des depassements de tampons, le mot
de passe est stocke en clair).


difficile certes mais dont on peux rencontrer la néccesité.
Cf ces serveurs d'auth. utilisant un AES mal codé qui ont été cassés
(pas le passphrase, les clés) en mesurant simplement les temps de calcul.

La methode en question consiste a mesurer le temps pris pour verifier
le mot de passe. Comme on arrete la boucle des qu'un caractere n'est
pas bon, en mesurant le temps on sait le nombre de caracteres bons en
tete qu'on a.

Cette methode a ete utilisee au moins une fois en pratique [...]


"une fois" ? non, c'est archi-classique et utilisé tous les jours,
tellement courant qu'il est normal de coder la contre-mesure.

Sylvain.

Avatar
Sylvain
Gabriel Dos Reis wrote on 22/06/2006 05:48:
Sylvain writes:

| Gabriel Dos Reis wrote on 22/06/2006 05:13:
| > et quel est le rapport avec l'âge du capitaine ?
| >
| et aussi qu'à la question: "Comment je peux faire
| pour que le programme teste un à un les caratères?"
|
| une réponse, un peu plus sécuritaire, est:
|
| const size_t PASS_LENGTH = ?; // could be a hash block size
|
| bool state = true;
| for (register int i = 0; i < PASS_LENGTH; ++i)
| state &= (candidate[i] == reference[i]);
|
| au moins ici le temps ne varira pas avec la saisie.

Parce que ?


parce que PASS_LENGTH est une constante ...
merci de formuler ta question plus précisemment si je l'ai mal comprise.

au cas où, "saisie" ne signifiait pas "temps de saisie à la main au
clavier", les pipes existent il me semble.

Sylvain.

Avatar
Arnaud Meurgues
Sylvain wrote:

une "implémentation balancée" garantirait que chaque cycle d'instruction
omis (du fait de la taille des registres, du nombre d'opérations
scalairisées (faite en simultanée) serait balancé par un NOP fournissant
un temps constant pour une opération donnée.
[...]

une telle implémentation de vérification de phrase de passe se
révèlerait donc un système très facile à casser.


Merci pour ces infos que j'ignorais (je n'ai jamais eu à me pencher sur
la question).

Question : l'introduction de nop aléatoires ne pourraient-ils pas être
plus simple tout en étant suffisante pour empêcher de déduire quoique ce
soit du temps d'analyse ?

Par ailleurs, comment est mesuré le temps d'analyse ? Entre le moment où
l'on entre le mot de passe et le moment où il est refusé ? Ou bien en
ayant un accès plus bas niveau de la machine ?

--
Arnaud

Avatar
Jean-Marc Bourguet
Arnaud Meurgues writes:

Question : l'introduction de nop aléatoires ne pourraient-ils pas être
plus simple tout en étant suffisante pour empêcher de déduire quoique ce
soit du temps d'analyse ?


De l'aleatoire asser sur pour ce genre d'application c'est difficile a
faire d'une part, d'autre part ca ne fait qu'ajouter du bruit qu'on
peut filtrer au prix d'un facteur constant.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Gabriel Dos Reis
Sylvain writes:

| Gabriel Dos Reis wrote on 22/06/2006 05:48:
| > Sylvain writes:
| > | Gabriel Dos Reis wrote on 22/06/2006 05:13:
| > | > et quel est le rapport avec l'âge du capitaine ?
| > | >
| > | et aussi qu'à la question: "Comment je peux faire
| > | pour que le programme teste un à un les caratères?"
| > | | une réponse, un peu plus sécuritaire, est:
| > | | const size_t PASS_LENGTH = ?; // could be a hash block size
| > | | bool state = true;
| > | for (register int i = 0; i < PASS_LENGTH; ++i)
| > | state &= (candidate[i] == reference[i]);
| > | | au moins ici le temps ne varira pas avec la saisie.
| > Parce que ?
|
| parce que PASS_LENGTH est une constante ...

Et alors, le mot de passe de reference a aussi une longeur constante.
Explication à revoir.

-- Gaby
Avatar
Sylvain
Gabriel Dos Reis wrote on 22/06/2006 17:56:

Et alors, le mot de passe de reference a aussi une longeur constante.
Explication à revoir.


ça parait gros! tu as du sauter sauter un épisode ?!? ou confondre
strcmp(s1, s2) avec memcmp(p1, p2, TheCommunFixedLength) ....


le PO indique:

reference = "12H12"
candidats = "1" ou "12"
instruction: strcmp(ref,candidate)

insinues-tu que strcmp(s1, s2) effecture toujours sur une comparaison
sur un nombre constant de caractères ?
si oui, lequel ? la taille de s1, celle de s2, la taille de la mémoire ?
je sèche un peu ...

pour rappel (je sais qu'il n'est pas besoin, mais soyons sur de parler
de la même chose), strcmp s'arrête nécessairement dès qu'une des 2
chaines est entièrement parcourue (ptr courant sur ).

pour compléter, string::operator==(string) n'a pas cette contrainte et
peut sortir en false sur la simple comparaison initiale des longueurs.

les 2 opèrent donc en temps non constant; temps proportionnel à la plus
petite chaine pour strcmp; temps variable (nul ou prop. à la longueur)
pour string=
Sylvain.

Avatar
Sylvain
Arnaud Meurgues wrote on 22/06/2006 16:05:

Question : l'introduction de nop aléatoires ne pourraient-ils pas être
plus simple tout en étant suffisante pour empêcher de déduire quoique ce
soit du temps d'analyse ?


non, générer du bruit peut être utilisé comme contre-mesure d'attaque de
conso ou de fréquences (cités par Arnauld) en faisant touner un
générateur en parallèle de l'instruction à masquer; mais c'est
inapplicable ici (j'entends pour un PC de bureau avec un seul CPU) et ne
servirait à rien pour l'aspect temps de traitement.

Par ailleurs, comment est mesuré le temps d'analyse ? Entre le moment où
l'on entre le mot de passe et le moment où il est refusé ? Ou bien en
ayant un accès plus bas niveau de la machine ?


en redirigeant les entrées (injection de mots de passe candidats) et les
sorties (prompt de demande de saisie) de ton programme et en
chronométrant simplement l'arrivé de la réponse après envoi d'un
candidat; et cela peut même être mener à distance via une entrée
ethernet si le programme peut être lancé sur compte anonyme (ou
authentifié); on fera évidemment plusieurs mesures pour les mêmes
valeurs tests et un peu de stats derrière mais si la charge de la
machine n'est pas réellement chaotique (elle ne l'est quasi jamais) on
aura ces temps.


pour conclure mes commentaires sur cette 'simple' vérification de
passphrase, je recommanderais de:

- stocker (une fois pour toute) un hash du passphrase référence (il peut
ne pas être génant de l'avoir en clair si le soft est vraiment protégé
en lecture, mais il vaut mieux éviter).

- boucler sur un nombre borné et petit d'essais (pas de while infini)

- faire la saisie via un conteneur protégé (tel string) ou en comptant
les caractères reçus

- hasher le passphrase candidat

- faire une boucle (binaire ou booléenne) sans shortcut et sur
l'intégralité de la taille des digest générés. (seule cette opération
est sensible au temps).

- introduire un temps d'attente croissant entre chaque essai raté

- bloquer le service pendant un temps significativement long après
épuisement du nombre d'essais avant de le réinitialiser (un service par
agent ayant autorité de déblocage est souvent non indispensable et
toujours pénible).

Sylvain.

Avatar
Sylvain
kanze wrote on 22/06/2006 11:17:

La question est plutôt : en quoi un code simple et correct
serait un plus ici ? Le code initialement posté plante
carrément dès que l'utilisateur entre quelque chose d'imprévu.
On peut bien le « corriger » pour qu'il marche, tout en
gardant le char[], mais à coût de pas mal de complications en
plus. Or qu'avec std::string, il marche comme il faut d'office.


évaluons les "pas mal de complication":

char pw[20]; // donné par le PO
int cnt = 0; // sorti nécessaire ensuite
for (; cnt < 20; ){
int c = getchar();
if (c == 'r' || c == 'n')
break;
pw[cnt++] = c;
}

5 lignes de code et une déclaration, grosse complication en effet.

note que je n'avais pas indiqué que string n'était pas un bon choix,
même si je sais, ne pas avoir vanter la STL est déjà une faute coupable

En fait, le seul cas que je peux imaginer où la solution avec
char[] a un avantage ici, c'est si on est payé à la ligne.


comment leur dire qu'il est des monde utilisant le langage C++ qui ne
disposent pas d'un runtime illimité et pas de sacro-sainte STL ??....

J'avoue ne pas comprendre ces histoires de « balancée » et
d'autres. C'est peut-être une /préjudice/ personnelle, mais je
"préférence" ?

trouve qu'une solution simple et correcte soit plus pertinante à
une solution complexée et erronée.

Quant à la performance : sur ma machine, les opérations sur
std::string sont toujours bien plus rapide que le temps
qu'il me faut pour tapper le texte en entrée. Au point
où on pourrait carrément ignorer leur temps d'exécution.


hein ?!? quel rapport avec les perf de saisies ? (de qui tape vite sans
regarder?)

%> leSoftQuiVerifieLePassphrase < unAnnuaireDePassphrase

ne marche pas chez toi ?

Sylvain.

1 2 3 4 5