expertenaustausch > comp.lang.* > comp.lang.php.misc

Frank Arthur (17.04.2004, 10:48)
Hi,

ich wollte mit einem regulaeren Ausdruck testen, ob ein eingegebenes
Passwort bestimmten Anforderungen genuegt.
1. Muss mindestens ein Zeichen von 0-9 sowie ein Zeichen von A-Z sowie
ein Zeichen von a-z enthalten.
2. Darf nicht mit 0-9 beginnen.

Das erste habe ich ja noch hinbekommen:
ereg ("[A-Z][a-z][0-9]", $password)
Das hat zwar den komischen Effekt, dass dreistellige Passwoerter immer
mit einem Grossbuchstaben beginnen, gefolgt von einem kleinbuchstaben,
gefolgt von einer Ziffer. Aber bei vierstelligen ist alles OK (und
dreistellige Passwoerter verbiete ich woanders).

Die Bedingung in 2. habe ich aber nicht geschafft einzubauen.

Hat jemand eine Idee?

tschau Frank.
Johannes Müller (17.04.2004, 11:17)
Frank Arthur schrieb:

[..]
> Die Bedingung in 2. habe ich aber nicht geschafft einzubauen.
> Hat jemand eine Idee?
> tschau Frank.


function pwd_chk($passwd)
{
/* prüft ob grossbuchstabe vorkommt */
$pattern1 = '/[A-Z]/';
$value1 = preg_match($pattern1,$passwd);

/* prüft ob kleiner buchstabe vorkommt */
$pattern2 = '/[a-z]/';
$value2 = preg_match($pattern2,$passwd);

/* prüft ob zahl vorkommt */
$pattern3 = '/[0-9]/';
$value3 = preg_match($pattern3,$passwd);

/* Verbietet Zahlen am anfang und leerzeichen im passwd */
$pattern4 = '/[^0-9][\S]+/';
$value4 = preg_match($pattern4,$passwd);

if($value1 && $value2 && $value3 && $value4){
return TRUE;
}

return FALSE;
}

....kürzer hab ichs nicht hinbekommen auf die schnelle, aber es sollte
trotzdem funktionieren.

*Hannes*
steffen horst (17.04.2004, 11:58)
Frank Arthur <no.email> wrote:
>ich wollte mit einem regulaeren Ausdruck testen, ob ein eingegebenes
>Passwort bestimmten Anforderungen genuegt.
>1. Muss mindestens ein Zeichen von 0-9 sowie ein Zeichen von A-Z sowie
>ein Zeichen von a-z enthalten.
>2. Darf nicht mit 0-9 beginnen.
>Das erste habe ich ja noch hinbekommen:
>ereg ("[A-Z][a-z][0-9]", $password)


8.2. Soll ich ereg() oder preg() verwenden?


>Das hat zwar den komischen Effekt, dass dreistellige Passwoerter immer
>mit einem Grossbuchstaben beginnen, gefolgt von einem kleinbuchstaben,
>gefolgt von einer Ziffer. Aber bei vierstelligen ist alles OK (und
>dreistellige Passwoerter verbiete ich woanders).


Das hat auch noch den weiteren Effekt, dass z.b. 0Aa3 zugelassen ist.
Ich würde sagen, Du musst leider alle Fälle getrennt abdecken:

1. [a-z]+[A-Z]+[0-9]+
2. [a-z]+[0-9]+[A-Z]+
3. [A-Z]+[a-z]+[0-9]+
4. [A-Z]+[0-9]+[a-z]+

Das sind die erlaubten Anfänge. Das ganze darf dann von [a-zA-Z0-9]*
gefolgt werden. Somit:

/([a-z]+[A-Z]+[0-9]+)|([a-z]+[A-Z]+[0-9]+)|([A-Z]+[a-z]+[0-9]+)|([A-Z]+[0-9]+[a-z]+)[a-zA-Z0-9]*/

Möglicherweise hab ich nun irgendwo eine Klammer vergessen, aber das
System erkennt man ja: /(1)|(2)|(3)|(4)[a-zA-Z0-9]*/

Du Länge kannst Du hier auch noch recht leicht einbauen. Du weisst,
dass 1, 2, 3 und 4 in jedem Fall die Länge 3 haben. Deshalb kannst Du
den letzten Teilausdruck noch so oft fordern, wie Du ihn möchtest.
Also beispielsweise:

/(1)|(2)|(3)|(4)[a-zA-Z0-9]{3,}/

Damit setzt Du eine Mindestlänge von 6 Zeichen fest. Es ist aber nicht
unsinnig, das getrennt und durch strlen() zu testen.

gruß, steffen
Frank Arthur (17.04.2004, 19:04)
steffen horst wrote:

>>ereg ("[A-Z][a-z][0-9]", $password)

>8.2. Soll ich ereg() oder preg() verwenden?
>


Guter Tip: bin auf preg_match umgestiegen.

>Ich würde sagen, Du musst leider alle Fälle getrennt abdecken:
>1. [a-z]+[A-Z]+[0-9]+
>2. [a-z]+[0-9]+[A-Z]+
>3. [A-Z]+[a-z]+[0-9]+
>4. [A-Z]+[0-9]+[a-z]+
>Das sind die erlaubten Anfänge. Das ganze darf dann von [a-zA-Z0-9]*
>gefolgt werden. Somit:
>/([a-z]+[A-Z]+[0-9]+)|([a-z]+[A-Z]+[0-9]+)|([A-Z]+[a-z]+[0-9]+)|([A-Z]+[0-9]+[a-z]+)[a-zA-Z0-9]*/


Vielen Dank fuer den Vorschlag!
Hast du nicht das Dach am Anfang vergessen?

Ich benutze jetzt:

"/^([A-Z]+[a-z]+[2-9]+)|([a-z]+[A-Z]+[2-9]+)|([A-Z]+[2-9]+[a-z]+)|([a-z]+[2-9]+[A-Z]+)/"

Das [a-zA-Z0-9]* am Ende schenke ich mir, weil ich an anderer Stelle
schon unerwuenschte Zeichen abfange. Die Kontrolle ist fuer ein
automatisch genieriertes passwort. Desshalb [2-9], weil ich u.a. null
und eins wegen Verwechslungsgefahr nicht verwende.
steffen horst (17.04.2004, 20:23)
Frank Arthur <no.email> wrote:
>steffen horst wrote:
>Vielen Dank fuer den Vorschlag!
>Hast du nicht das Dach am Anfang vergessen?


joup

>Ich benutze jetzt:
>"/^([A-Z]+[a-z]+[2-9]+)|([a-z]+[A-Z]+[2-9]+)|([A-Z]+[2-9]+[a-z]+)|([a-z]+[2-9]+[A-Z]+)/"
>Das [a-zA-Z0-9]* am Ende schenke ich mir, weil ich an anderer Stelle
>schon unerwuenschte Zeichen abfange. Die Kontrolle ist fuer ein
>automatisch genieriertes passwort. Desshalb [2-9], weil ich u.a. null
>und eins wegen Verwechslungsgefahr nicht verwende.


Mit O und l? Dann solltest Du logischerweise auch auf O und l
verzichten, denn sonst bleibt das Problem ja bestehen.

grüße, steffen
Frank Arthur (18.04.2004, 11:34)
steffen horst wrote:
>Mit O und l? Dann solltest Du logischerweise auch auf O und l
>verzichten, denn sonst bleibt das Problem ja bestehen.


Habe ich auch!
Uebrigens ist oben noch ein Fehler (klammer vergessen). Es muss
heissen:
"/^(([A-Z]+[a-z]+[2-9]+)|([a-z]+[A-Z]+[2-9]+)|([A-Z]+[2-9]+[a-z]+)|([a-z]+[2-9]+[A-Z]+))/"

Hier der komplette code zum automatischen generieren eines Passwortes:

function generate_password ($digits = 8, $format = NULL)
{
static $chars =
"0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpq rstuvwxyz23456789";
$result = "";
if ($digits < 3) $digits = 3;
switch ($format)
{
case "C":
$min = 10;
$max = 57;
$regstr = "/^(([A-Z]+[a-z]+)|([a-z]+[A-Z]+))/";
break;
case "L":
$min = 34;
$max = 65;
$regstr = "/^([a-z]+[2-9]+)/";
break;
case "N":
$min = 0;
$max = 9;
$regstr = "/^[1-9]/";
break;
case "U":
$min = 2;
$max = 33;
$regstr = "/^([A-Z]+[2-9]+)/";
break;
default:
$min = 2;
$max = 65;
$regstr =
"/^(([A-Z]+[a-z]+[2-9]+)|([a-z]+[A-Z]+[2-9]+)|([A-Z]+[2-9]+[a-z]+)|([a-z]+[2-9]+[A-Z]+))/";
break;
}
for ($i = 0; $i < $digits; ++$i)
$result .= $chars[rand ($min ,$max)];
return preg_match ($regstr, $result) ? $result : generate_password
($digits, $format);
}
steffen horst (18.04.2004, 20:33)
Frank Arthur <no.email> wrote:
>steffen horst wrote:
>Uebrigens ist oben noch ein Fehler (klammer vergessen). Es muss
>heissen:
>"/^(([A-Z]+[a-z]+[2-9]+)|([a-z]+[A-Z]+[2-9]+)|([A-Z]+[2-9]+[a-z]+)|([a-z]+[2-9]+[A-Z]+))/"


Warum?

>Hier der komplette code zum automatischen generieren eines Passwortes:
>[...]


Du könntest alternativ auch das Passwort erst zufällig erzeugen lassen
und dann die drei Zeichen nachträglich an zufälligen Stellen einbauen.
Aber wenn's so funktioniert...

>[...]
> for ($i = 0; $i < $digits; ++$i)
> $result .= $chars[rand ($min ,$max)];
> return preg_match ($regstr, $result) ? $result : generate_password
>($digits, $format);
>}


while wäre sicherlich die schnellere Lösung.

Grüße, Steffen
Frank Arthur (19.04.2004, 22:03)
>>Uebrigens ist oben noch ein Fehler (klammer vergessen). Es muss
>>heissen:
>>"/^(([A-Z]+[a-z]+[2-9]+)|([a-z]+[A-Z]+[2-9]+)|([A-Z]+[2-9]+[a-z]+)|([a-z]+[2-9]+[A-Z]+))/"

>Warum?


Weil sich sonst das Dach fuer den Dateianfang nur auf die erste
klammer bezieht. (Hatte es erst ohne Klammern versucht und mich
gewundert, warum es nicht funktioniert.)

>while wäre sicherlich die schnellere Lösung.


Bist du dir sicher?
Bin ein alter Mac C/C++ Programmierer und dort waere diese
for-Schleife schneller, weil die variable $i besser in den
Prozessorcache gehalten werden kann.
normalerweise haette ich sogar ($i = 0, $z = $digits; $i < $z; ++$i)
benutzt, weil dann auch $z besser gecached werden kann. Aber weil ich
mir bei PHP nicht so sicher bin und solcherlei Optimierungen fuer
einen so Zeitunkritischen Vorgang kaum noetig sind habe ich es
gelassen.
Ich mag for-Schleifen lieber, weil der Code damit leichter zu lesen
ist.

Hab gerad mal nachgeschaut, bei php.net hat jemand mal getestet:
do while: 100.00 percent of ~ 0.0267 seconds
while: 0.036508817672729 seconds, 136.85 percent of ~ 0.0267 seconds
for: 0.037308998107910 seconds, 139.85 percent of ~ 0.0267 seconds
foreach: 0.064857139587402 seconds, 243.11 percent of ~ 0.0267 seconds

tschau und nochmal Danke fuer deine Hilfen.
Frank.
steffen horst (20.04.2004, 01:03)
Frank Arthur <no.email> wrote:
>Weil sich sonst das Dach fuer den Dateianfang nur auf die erste
>klammer bezieht. (Hatte es erst ohne Klammern versucht und mich
>gewundert, warum es nicht funktioniert.)


Ach mit dem von mir vergessenem ^, ja :)

>Bist du dir sicher?
>Bin ein alter Mac C/C++ Programmierer und dort waere diese
>for-Schleife schneller, weil die variable $i besser in den
>Prozessorcache gehalten werden kann.


Nicht die For-Schleife, ich meine die Rekursion. Vielleicht besser so:

do
{
for ($i=0; $i < $digits; ++$i)
{
$result .= $chars[rand($min ,$max)];
}
}
while (!preg_match($regstr, $result));

return $result;

Am schnellsten wäre aber wohl die andere von mir vorgeschlagene
Möglichkeit, die drei erforderlichen Zeichen nach dem Generieren des
Rests einzubauen. Also erst Passwort erzeugen und dann an 3 zufälligen
(unterschiedlichen) Stellen einen Großbuchstaben, einen
Kleinbuchstaben und eine Zahl (die nicht am Anfang stehen darf)
einzubauen. Aber das würde Code ergeben, der 1. unschön, 2.
unverständlich und 3. unübersichtlich wäre. Damit ist Deine Lösung
sehr zu bevorzugen, es sei denn, Du planst einen Passwort-Server zu
bauen, der pro Sekunde 500 Milliarden Passwörter erzeugt.

>Hab gerad mal nachgeschaut, bei php.net hat jemand mal getestet:
>do while: 100.00 percent of ~ 0.0267 seconds
>while: 0.036508817672729 seconds, 136.85 percent of ~ 0.0267 seconds
>for: 0.037308998107910 seconds, 139.85 percent of ~ 0.0267 seconds
>foreach: 0.064857139587402 seconds, 243.11 percent of ~ 0.0267 seconds


Das meinte ich sicherlich nicht, aber ist ja trotzdem interessant.
Find es seltsam, dass sich do while und while unterscheiden. Und warum
for langsamer ist, kann ich auch nicht nachvollziehen. Vielleicht eine
zu unrepräsentative Messung?

grüße, steffen
Frank Arthur (20.04.2004, 14:22)
>>>>[...]
[..]
>Kleinbuchstaben und eine Zahl (die nicht am Anfang stehen darf)
>einzubauen. Aber das würde Code ergeben, der 1. unschön, 2.
>unverständlich und 3. unübersichtlich wäre.


Du hast Recht, weil die Funktion preg_match ja auch Rechenzeit kostet.
Das Einfuegen an unterschiedlichen Stellen macht das ganze doch
unnoetig kompliziert. Dann waehren eine zusaetzliche Schleife und
vielleicht noch ein switch-Konstrukt noetig.

>Damit ist Deine Lösung
>sehr zu bevorzugen, es sei denn, Du planst einen Passwort-Server zu
>bauen, der pro Sekunde 500 Milliarden Passwörter erzeugt.


Wahrscheinlich wird sie kaum jemand benutzen und zu einer
Technikstudie verkommen.

>Das meinte ich sicherlich nicht, aber ist ja trotzdem interessant.
>Find es seltsam, dass sich do while und while unterscheiden. Und warum
>for langsamer ist, kann ich auch nicht nachvollziehen. Vielleicht eine
>zu unrepräsentative Messung?


Das ist wahrscheinlich weil PHP eine scriptsprache ist und sich
vielleicht jemand mehr Muehe gegeben hat die do-while-Schleife zu
optimieren.
steffen horst (20.04.2004, 15:04)
Frank Arthur <no.email> wrote:
>Du hast Recht, weil die Funktion preg_match ja auch Rechenzeit kostet.
>Das Einfuegen an unterschiedlichen Stellen macht das ganze doch
>unnoetig kompliziert. Dann waehren eine zusaetzliche Schleife und
>vielleicht noch ein switch-Konstrukt noetig.


Es geht nicht um preg_match(), sondern darum, dass im worst-case Deine
Generierung unendlich lange dauert :)

In Deinem Fall erzeugst Du solange Passwörter, bis der reguläre
Ausdruck passt. Die andere Idee ist, nur 1x ein Passwort zu erzeugen
und dafür Sorge zu tragen, dass es in jedem Fall die drei
Anforderungen erfüllt.

grüße, steffen
Ähnliche Themen