Kreditkartenprüfung Luhnalgorythmus->Probleme
Bobby
- php
0 Alexander (HH)0 Bobby
Moin
ich will Kreditkartennummer auf den Luhnalgorythmus prüfen. Dazu nutz eich folgende (nicht von mir stammende) Funktion:
// Is the number valid against luhn?
$cardNumber = strrev($this->__ccNum);
$numSum = 0;
for($i = 0; $i < strlen($cardNumber); $i++)
{
$currentNum = substr($cardNumber, $i, 1);
if(floor($currentNum / 2) != $currentNum / 2)
{
$currentNum *= 2;
}
if(strlen($currentNum) == 2)
{
$firstNum = substr($currentNum, 0, 1);
$secondNum = substr($currentNum, 1, 1);
$currentNum = $firstNum + $secondNum;
}
$numSum += $currentNum;
}
// If the total has no remained its OK
$passCheck = ($numSum % 10 == 0 ? true : false);
leider ist passCheck auch mit reelen Kartennummern false. Ich verstehe leider nicht ganz wieso. Wer kann mir helfen?
Gruß Bobby
Moin Moin!
Moin
ich will Kreditkartennummer auf den Luhnalgorythmus prüfen.
Der Wikipedia-Artikel mit *SAUBEREN* Code-Beispielen in diversen Sprachen ist Dir bekannt?
Dazu nutz eich folgende (nicht von mir stammende) Funktion:
// Is the number valid against luhn?
\*SCHAUDER\*
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
Moin
Der Wikipedia-Artikel mit *SAUBEREN* Code-Beispielen in diversen Sprachen ist Dir bekannt?
Gab es bei Kreditkartennummern nicht ein paar Sonderregeln? Die stehen glaub ich beim Wikipedia-Artikel nicht mit bei. Ich hab irgendwas mit Prüfung ab 2 Nummer und bis zu rVorletzten. Kann mich da aber auch irren...
Dazu nutz eich folgende (nicht von mir stammende) Funktion:
// Is the number valid against luhn?
>
> \*SCHAUDER\*
Wieso schauderts dich da?
Erklärung: Kreditkartenvalidierung hab ich bis jetzt durch Fremdanbieter übernehmen lassen. Die kosten aber leider Geld. Deswegen möcht ichs gern selbst versuchen und bin so ganz noch nicht dahinter gestiegen.
Gruß Bobby
--
-> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
-> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
Moin!
Erklärung: Kreditkartenvalidierung hab ich bis jetzt durch Fremdanbieter übernehmen lassen. Die kosten aber leider Geld. Deswegen möcht ichs gern selbst versuchen und bin so ganz noch nicht dahinter gestiegen.
Der Prüfsummenalgorithmus unterscheidet sich nach meinem Wissen je nach CC-Gesellschaft.
Außerdem dürfte eine externe Validierung vermutlich tatsächlich Kontakt zur ausgebenden Gesellschaft herstellen und dabei auch die Angabe der Gültigkeitsdaten prüfen - sowas kannst du prinzipbedingt nicht selbst herstellen.
- Sven Rautenberg
Außerdem dürfte eine externe Validierung vermutlich tatsächlich Kontakt zur ausgebenden Gesellschaft herstellen und dabei auch die Angabe der Gültigkeitsdaten prüfen - sowas kannst du prinzipbedingt nicht selbst herstellen.
Der Luhn-Algorithmus sollte bei allen Kreditkarten gleich sein. Die Karten unterscheiden sich, soweit ich das weiss seit meiner letzten Kreditkarten-Prüf-Routine, nur in der Länger der Karte (zwischen 12 und 16 Zeichen, sowas in der Art) und den erste 2-4 Zahlen. Die ersten 2-4 Zahlen geben Informationen über das Kreditinstitut.
Ansonsten ist der Luhn-Algorithmus einzig und alleine dazu da, die ganz dummen "Betrüger" abzuschrecken. Es ist relativ einfach, sich eine beliebige gültige Nummer auszudenken; man vergrault nur die Spontanen, die sich mal eben denken, ha, dem geb ich irgendwelche Schmarrn Daten, vielleicht kann ich ja was abgreifen.
Gruß Ben
Moin Moin!
Dazu nutz eich folgende (nicht von mir stammende) Funktion:
// Is the number valid against luhn?
> >
> > \*SCHAUDER\*
>
> Wieso schauderts dich da?
Weil gerade einmal drei Zeilen sauber sind: Eine Initialisierung, die Verdoppelung, und das Aufsummieren der Ziffern bzw. Quersummen.
> ~~~php
> // Is the number valid against luhn?
> $cardNumber = strrev($this->__ccNum);
Eingabe umkehren und kopieren, egal wie lang sie ist. Ich unterstelle bei PHP mal, dass wir über ein Webserver-Environment reden. Die Eingabe kommt also sehr wahrscheinlich von einem nicht kontrollierbarem Programm und kann auch mal ein paar Megabyte groß sein. Diese Operation verdoppelt mal eben den Speicherbedarf.
Die Umkehrung ist noch nicht einmal notwendig, der PHP-Code in der Wikipedia zeigt schön, dass es ohne geht.
Wie lang ist eine Kartennummer? Ist eine ein Zeichen lange Kartennummer gültig? Oder eine 200 Zeichen lange Nummer?
Warum fängt die Routine nicht mit so einer Prüfung an? return false if (strlen($cardNumber)<$minLen) or (strlen($cardNumber)>$maxlen;
Danach könnte man darüber nachdenken, Daten umzukopieren und Schleifen laufen zu lassen.
> $numSum = 0;
Kein Einwand.
> for($i = 0; $i < strlen($cardNumber); $i++)
> {
> $currentNum = substr($cardNumber, $i, 1);
Können "Ä", "$" oder "ß" Bestandteil einer gültigen Nummer sein? Nein? Warum steht hier dann nicht ein sofortiger Abbruch, wenn $currentNum keine Ziffer ist?
return false unless $currentNum>="0" and $currentNum<="9";
(Funktioniert so nur mit ASCII und kompatiblen Zeichensätzen, bei denen "0" bis "9" im Zeichensatz direkt aufeinander folgen)
Alternativ könnte der Test vor der for-Schleife in einer RegExp laufen, die könnte auch gleich die Länge prüfen:
return false unless $cardNumber=~/^\d{8,12}$/;
8 wäre die Untergrenze für die Länge, 12 die Obergrenze.
Wenn nur 8 oder 12 erlaubt sind:
return false unless $cardNumber=~/^\d{8}(\d{4})?$/;
(Lies: Acht Ziffern, optional gefolgt von vier weiteren Ziffern.)
> if(floor($currentNum / 2) != $currentNum / 2)
Ein ein Zeichen langer String mit einer Ziffer wird zweimal in eine Zahl umgewandelt, zweimal mit einer Nicht-Integer-Division durch zwei geteilt, einmal gerundet, nur um dann herauszufinden, ob die Zahl gerade oder ungerade ist. Wie kompliziert kann man sich um eine Integer-Modulo-Division herumwerkeln?
if ($currentNum%2 > 0)
ASCII vorausgesetzt würde sogar eine Bit-Operation direkt auf dem Zeichen ausreichen.
> {
> $currentNum *= 2;
Der ein Zeichen lange String mit der Ziffer wird noch einmal in eine Zahl umgewandelt und verdoppelt, soweit ok.
> }
> if(strlen($currentNum) == 2)
$currentNum enthält eine gerade Zahl zwischen 0 und 18.
Wikipedia sagt: "Für jede Ziffer aus welcher 10 oder mehr wird, bilde die Quersumme (heißt: addiere die einzelnen Ziffern)."
Es ist für den Urheber also völlig naheliegend, die Zahl wieder in einen String umzuwandeln und dessen Länge mit 2 zu vergleichen. Mit der Zahl 10 zu vergleichen ist viel zu offensichtlich.
Davon abgesehen KANN $currentNum dann und nur dann größer oder gleich 10 sein, wenn $currentNum vorher verdoppelt wurde. Diese Fallunterscheidung kann also in den vorgerigen if-Block hineingezogen werden, das erspart in der Hälfte aller Fälle den zweiten Vergleich. Notwendig ist es nicht, aber umständlicher.
if ($currentNum>=10)
> {
$currentNum ist hier immer eine Zahl, durch die Multiplikation mit 2 im vorherigen Schritt.
> $firstNum = substr($currentNum, 0, 1);
Wir wandeln sie in einen String, nehmen dessen erstes Zeichen, und packen es in eine neue Variable.
> $secondNum = substr($currentNum, 1, 1);
Wir wandeln sie noch einmal in einen String, nehmen dessen zweites Zeichen, und packen es in eine weitere Variable.
> $currentNum = $firstNum + $secondNum;
Dann wandeln wir die beiden Strings, wandeln sie jeweils in Zahlen um, und addieren sie.
Integer-Division und Modulo-Operator bei einem auf der Modulo-Operation basierenden Prüfung zu verwenden ist dem Autoren offensichtlich zu trivial.
Es geht ohne String-Umwandlungen und Hilfsvariablen.
$currentNum=int($currentNum/10)+($currentNum%10);
> }
> $numSum += $currentNum;
Nichts auszusetzen.
> }
>
> // If the total has no remained its OK
> $passCheck = ($numSum % 10 == 0 ? true : false);
Ein Modulo-Operator! Der einzige im ganzen Code, der bekanntlich eine Prüfsumme auf Modulo-Basis berechnet!
Was liefert der Vergleichsoperator? Vielleicht einen boolschen Wert? Man weiß es nicht so genau, sicherheitshalber machen wir basierend auf dem boolschen Wert des Vergleichs noch einen test, um dann ganz sicher boolsche Werte zu haben. Ich will nicht nachvollziehen, was im Kopf des Urhebers herumspukte, als er diese Zeilen schrieb.
return ($numSum % 10)==0;
>
(Randbemerkung: Meine Code-Fetzen sind in Perl-Syntax. Macht der Gewohnheit.)
Alexander
Moin
Wie lang ist eine Kartennummer? Ist eine ein Zeichen lange Kartennummer gültig? Oder eine 200 Zeichen lange Nummer?
|
Warum fängt die Routine nicht mit so einer Prüfung an? return false if (strlen($cardNumber)<$minLen) or (strlen($cardNumber)>$maxlen;
Danach könnte man darüber nachdenken, Daten umzukopieren und Schleifen laufen zu lassen.
Das wird dovch vorher schon geprüft. Mit den Funktionen wollte ich nicht den Thread zumüllen. Das problem liegt nur an dieser Luhn-Algorythmus-Funktion. Selbstverständlich wird vorher alles mögliche abgeprüft!!!
Also war mir das keine wirkliche Hilfe. Aber trotzdem Danke
Gruß Bobby
Moin Moin!
Moin
Wie lang ist eine Kartennummer? Ist eine ein Zeichen lange Kartennummer gültig? Oder eine 200 Zeichen lange Nummer?
|Warum fängt die Routine nicht mit so einer Prüfung an? return false if (strlen($cardNumber)<$minLen) or (strlen($cardNumber)>$maxlen;
Danach könnte man darüber nachdenken, Daten umzukopieren und Schleifen laufen zu lassen.
Das wird dovch vorher schon geprüft.
Und exakt wo sehe ich das in dem geposteten Ko... -- äh - Code?
Mit den Funktionen wollte ich nicht den Thread zumüllen. Das problem liegt nur an dieser Luhn-Algorythmus-Funktion. Selbstverständlich wird vorher alles mögliche abgeprüft!!!
Warum?
Die Aufgabe dieser Funktion ist es, zu prüfen, ob ein String eine gültige Kreditkartennnummer ist oder nicht. Dazu gehört meiner Meinung nach als erstes eine Prüfung, ob der String überhaupt die Basisanforderungen Länge und erlaubte Zeichen erfüllt.
Also war mir das keine wirkliche Hilfe. Aber trotzdem Danke
Du hast gefragt, warum mir der Code nicht gefällt. Die Antwort hast Du bekommen. Als Hilfe war das nicht gemeint. Hilfreich gemeint ist der Wikipedia-Artikel in meinem ersten Posting.
Alexander
Hi,
Wenn schon verbessern, dann auch konsequent ;-)
if ($currentNum>=10)
{
$currentNum ist hier immer eine Zahl, durch die Multiplikation mit 2 im vorherigen Schritt.
Da die Zahl aus der Verdoppelung einer einzigen Ziffer entstanden ist und größer als 9 ist, ist die erste Ziffer immer eine 1 (2*9 ist 18)
Das umständliche Ermitteln der ersten Ziffer aus dem String kann also komplett entfallen, die erste Ziffer ist 1.
$secondNum = substr($currentNum, 1, 1);
$currentNum = $firstNum + $secondNum;
Die erste Ziffer braucht nicht ermittelt zu werden, die kann fix als 1 angesetzt werden, da $currentNum ja >= 10 ist, und maximal 18 sein kann.
d.h.
$currentNum = 1 + $secondNum;
cu,
Andreas