Home > Archive > PHP DB > December 2007 > Re: [PHP-DB] Credit Card Encryption
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Re: [PHP-DB] Credit Card Encryption
|
|
| Daniel Brown 2007-12-19, 7:00 pm |
| On Dec 19, 2007 2:41 AM, Keith Spiller <larentium@hosthive.com> wrote:
> Ok I've done some research and some thinking. What about storing orders in
> the database (product info and customer info) and then using GnuPG or PGP to
> send the credit card info to the merchant? This way the credit card
> information is not stored on the server or in the database but only in
> printed format by the merchant. Since my client processes all of the credit
> card orders by hand this seems like an ideal solution.
I had a client that did offline (manual) processing of credit card
orders as well. With liability issues and the problems that others
have already pointed out, storing the credit card information was not
an option, yet my client still needed some way of having the data
available offline.
Consider the following:
ISSUER LENGTH
Diner's Club/Carte Blanche 14
American Express 15
VISA 13 or 16
MasterCard 16
Discover 16
Security checks aside (like making sure they selected the type of
card and that it matched the algorithm - VISA beginning with 4 and
being strlen($_POST['cardnum']) == 13 or 16, MasterCard being 16,
beginning with 51xx to 55xx, et cetera), I then had a hybrid of
storage and delivery.
Mail the first <? rand(4,6); ?> digits to the sales email
address(es) on file. Three addresses on two domains were used for
redundancy in this case. Store the remaining digits in the database.
You could write your own encryption algorithm or use one that is
publicly-available and reversible (Blowfish is what I was using, at
128, key length of 56 lower ASCII characters, padded with 7 on the key
and four on the output - MD5, SHA1, et al are NOT options here).
The sales department then received the first digits of the credit
card number via email, which stated it was an order key. Again, in my
case, I wrote an algorithm that would encrypt these digits prior to
sending, using the actual order number as a key. The accounting
software I wrote (all in PHP) would then retrieve the latter half of
the credit card number from the database, decrypt the first part of
the credit card number from the email (entered by the sales team on an
SSL-encrypted page), and the credit card number would be displayed in
full on the screen, to print, process, or verify.
The downside is that, if there are any problems with email and
delivery, the first $n digits of the card might not be received by the
sales department. While, to date, I'm not aware of this having been a
problem for my client (knock on wood), it's still a possibility. For
this reason, you need to be sure to either have the email address
confirmed prior to processing the order, or require a valid telephone
number, so that you can reach the customer in the event of a failure.
To assure the customer that you are calling legitimately, you will
still have the last digits of the credit card, as well as the
expiration data and CVV number (also stored in the database), the
billing address, and the date and time the order was placed.
It may not work for you, but that's how I created the system for
my client in 2004, and it's still being used today, with almost $8
Million in online sales. [pats self on back] ;-P
Now if I could just go back and renegotiate my contract for that gig....
--
Daniel P. Brown
[Phone Numbers Go Here!]
[They're Hidden From View!]
If at first you don't succeed, stick to what you know best so that you
can make enough money to pay someone else to do it for you.
| |
| Jason Gerfen 2007-12-19, 7:00 pm |
| -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Daniel Brown wrote:
> On Dec 19, 2007 2:41 AM, Keith Spiller <larentium@hosthive.com> wrote:
>
> I had a client that did offline (manual) processing of credit card
> orders as well. With liability issues and the problems that others
> have already pointed out, storing the credit card information was not
> an option, yet my client still needed some way of having the data
> available offline.
>
> Consider the following:
>
> ISSUER LENGTH
> Diner's Club/Carte Blanche 14
> American Express 15
> VISA 13 or 16
> MasterCard 16
> Discover 16
>
> Security checks aside (like making sure they selected the type of
> card and that it matched the algorithm - VISA beginning with 4 and
> being strlen($_POST['cardnum']) == 13 or 16, MasterCard being 16,
> beginning with 51xx to 55xx, et cetera), I then had a hybrid of
> storage and delivery.
>
> Mail the first <? rand(4,6); ?> digits to the sales email
> address(es) on file. Three addresses on two domains were used for
> redundancy in this case. Store the remaining digits in the database.
> You could write your own encryption algorithm or use one that is
> publicly-available and reversible (Blowfish is what I was using, at
> 128, key length of 56 lower ASCII characters, padded with 7 on the key
> and four on the output - MD5, SHA1, et al are NOT options here).
>
> The sales department then received the first digits of the credit
> card number via email, which stated it was an order key. Again, in my
Using the order number as the key is bad practice. Here is a random key
generator that you could use for your public/private keys and still use
the blowfish cipher as your method of encrypting:
<?PHP
function ReadFolder( $folder )
{
if( ( empty( $folder ) ) || ( !is_dir( $folder ) ) ) {
$rand_image = GenerateError( "Couldn't open directory" );
} else {
$rand_image = array();
if( $handle = opendir( $folder ) ) {
while( false !== ( $file = readdir( $handle ) ) ) {
if( $file != "." && $file != ".." && $file != "index.html" &&
!is_dir( $file ) ) {
$rand_image[] = $file;
}
}
closedir( $handle );
}
}
return $rand_image;
}
function MakeSuperRandom()
{
return srand( ( double ) microtime( time() ) * 100000 );
}
function PickRandomImages( $array )
{
$num1 = count( $array );
$num1 = $num1 - 1;
MakeSuperRandom();
$img_num = rand( 3, $num1 );
$image[] = $array[$img_num];
$num2 = count( $array );
$num2 = $num2 - 1;
MakeSuperRandom();
$img_num = rand( 3, $num2 );
$image[] = $array[$img_num];
$num3 = count( $array );
$num3 = $num3 - 1;
MakeSuperRandom();
$img_num = rand( 3, $num3 );
$image[] = $array[$img_num];
return $image;
}
function ChkArray( $array )
{
if( ( empty( $array ) ) || ( count( $array ) > 3 ) ) {
$data = 1;
} else {
$data = 0;
}
return $data;
}
function GeneratePrivKey( $array )
{
if( empty( $array ) ) {
$data = GenerateError( "Missing data for GeneratePrivKey function." );
} else {
for( $x = 0; $x < count( $array ); $x++ ) {
$keys[] = mhash( MHASH_SHA1, sha1( $array[$x] ) );
}
for( $y = 0; $y < count( $keys ); $y++ ) {
if( count( $keys ) == $keys[$y] ) {
$data .= $keys[$y];
} else {
$data .= $keys[$y] . ":";
}
}
}
return $data;
}
function GeneratePubKey( $data )
{
return md5( $data );
}
function EncData( $data, $key )
{
$td = mcrypt_module_open( 'rijndael-256', '', 'ofb', '' );
$iv = mcrypt_create_iv( mcrypt_enc_get_iv_size( $td ), MCRYPT_DEV_RANDOM );
$ks = mcrypt_enc_get_key_size( $td );
@mcrypt_generic_init( $td, $key, $iv );
$encrypted = mcrypt_generic( $td, $data );
echo "<br><b>Ciphered Text using Random Image Hash as Key:</b><pre> " .
$encrypted . "</pre><br>";
@mcrypt_generic_deinit( $td );
@mcrypt_generic_init( $td, $key, $iv );
$decrypted = mdecrypt_generic( $td, $encrypted );
echo "<br><b>De-Ciphered Text using Random Image Hash as Key:</b><pre>"
.. $decrypted . "</pre>";
@mcrypt_generic_deinit( $td );
@mcrypt_module_close( $td );
}
// to use functions
$x = ReadFolder( "images/" );
$y = PickRandomImages( $x );
$b = GeneratePrivKey( $y );
echo "<b>Private Key data:</b><pre>" . $b . "</pre>";
$data = "<br>" . GeneratePubKey( $b );
echo "<b>Public Key data:</b><pre>"; print_r( $data ); echo "</pre>";
echo EncData( $credit_card_data, $b );
?>
With that code you will have to re-write the 'EncData()' function to
perform ONLY encryption as of right now it encrypts and decrypts for
demonstration purposes only.
And on another note why not use a different delivery method altogether
such as using java-script to encrypt the data prior to transmission,
store the private key inside the local network, use the public key and
associate it with the purchase within the database and develop a method
of authentication for the users to retrieve the data and then, and only
then use the private key to decrypt the data.
Just a thought.
> case, I wrote an algorithm that would encrypt these digits prior to
> sending, using the actual order number as a key. The accounting
> software I wrote (all in PHP) would then retrieve the latter half of
> the credit card number from the database, decrypt the first part of
> the credit card number from the email (entered by the sales team on an
> SSL-encrypted page), and the credit card number would be displayed in
> full on the screen, to print, process, or verify.
>
> The downside is that, if there are any problems with email and
> delivery, the first $n digits of the card might not be received by the
> sales department. While, to date, I'm not aware of this having been a
> problem for my client (knock on wood), it's still a possibility. For
> this reason, you need to be sure to either have the email address
> confirmed prior to processing the order, or require a valid telephone
> number, so that you can reach the customer in the event of a failure.
> To assure the customer that you are calling legitimately, you will
> still have the last digits of the credit card, as well as the
> expiration data and CVV number (also stored in the database), the
> billing address, and the date and time the order was placed.
>
> It may not work for you, but that's how I created the system for
> my client in 2004, and it's still being used today, with almost $8
> Million in online sales. [pats self on back] ;-P
>
> Now if I could just go back and renegotiate my contract for that gig....
>
- --
Jason Gerfen
"I practice my religion
while stepping on your
toes..."
~The Ditty Bops
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFHaUTR5vk8bwKVAaIRAlPOAJoCUbI6rVCv
hG6pvuIzWTkbiyLVQgCfdE26
tJf77knhJ3p6q7DHsvZTWQc=
=wSva
-----END PGP SIGNATURE-----
| |
| Jason Gerfen 2007-12-19, 7:00 pm |
| -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Jason Gerfen wrote:
> Daniel Brown wrote:
>
>
>
>
>
>
> Using the order number as the key is bad practice. Here is a random key
> generator that you could use for your public/private keys and still use
> the blowfish cipher as your method of encrypting:
>
> <?PHP
> function ReadFolder( $folder )
> {
> if( ( empty( $folder ) ) || ( !is_dir( $folder ) ) ) {
> $rand_image = GenerateError( "Couldn't open directory" );
> } else {
> $rand_image = array();
> if( $handle = opendir( $folder ) ) {
> while( false !== ( $file = readdir( $handle ) ) ) {
> if( $file != "." && $file != ".." && $file != "index.html" &&
> !is_dir( $file ) ) {
> $rand_image[] = $file;
> }
> }
> closedir( $handle );
> }
> }
> return $rand_image;
> }
>
> function MakeSuperRandom()
> {
> return srand( ( double ) microtime( time() ) * 100000 );
> }
>
> function PickRandomImages( $array )
> {
> $num1 = count( $array );
> $num1 = $num1 - 1;
> MakeSuperRandom();
>
> $img_num = rand( 3, $num1 );
> $image[] = $array[$img_num];
>
> $num2 = count( $array );
> $num2 = $num2 - 1;
> MakeSuperRandom();
>
> $img_num = rand( 3, $num2 );
> $image[] = $array[$img_num];
>
> $num3 = count( $array );
> $num3 = $num3 - 1;
> MakeSuperRandom();
>
> $img_num = rand( 3, $num3 );
> $image[] = $array[$img_num];
> return $image;
> }
>
> function ChkArray( $array )
> {
> if( ( empty( $array ) ) || ( count( $array ) > 3 ) ) {
> $data = 1;
> } else {
> $data = 0;
> }
> return $data;
> }
>
> function GeneratePrivKey( $array )
> {
> if( empty( $array ) ) {
> $data = GenerateError( "Missing data for GeneratePrivKey function." );
> } else {
> for( $x = 0; $x < count( $array ); $x++ ) {
> $keys[] = mhash( MHASH_SHA1, sha1( $array[$x] ) );
> }
> for( $y = 0; $y < count( $keys ); $y++ ) {
> if( count( $keys ) == $keys[$y] ) {
> $data .= $keys[$y];
> } else {
> $data .= $keys[$y] . ":";
> }
> }
> }
> return $data;
> }
>
> function GeneratePubKey( $data )
> {
> return md5( $data );
> }
>
> function EncData( $data, $key )
> {
> $td = mcrypt_module_open( 'rijndael-256', '', 'ofb', '' );
> $iv = mcrypt_create_iv( mcrypt_enc_get_iv_size( $td ), MCRYPT_DEV_RANDOM );
> $ks = mcrypt_enc_get_key_size( $td );
> @mcrypt_generic_init( $td, $key, $iv );
> $encrypted = mcrypt_generic( $td, $data );
> echo "<br><b>Ciphered Text using Random Image Hash as Key:</b><pre> " .
> $encrypted . "</pre><br>";
> @mcrypt_generic_deinit( $td );
> @mcrypt_generic_init( $td, $key, $iv );
> $decrypted = mdecrypt_generic( $td, $encrypted );
> echo "<br><b>De-Ciphered Text using Random Image Hash as Key:</b><pre>"
> . $decrypted . "</pre>";
> @mcrypt_generic_deinit( $td );
> @mcrypt_module_close( $td );
> }
>
> // to use functions
> $x = ReadFolder( "images/" );
> $y = PickRandomImages( $x );
> $b = GeneratePrivKey( $y );
> echo "<b>Private Key data:</b><pre>" . $b . "</pre>";
> $data = "<br>" . GeneratePubKey( $b );
> echo "<b>Public Key data:</b><pre>"; print_r( $data ); echo "</pre>";
> echo EncData( $credit_card_data, $b );
>
> ?>
>
> With that code you will have to re-write the 'EncData()' function to
> perform ONLY encryption as of right now it encrypts and decrypts for
> demonstration purposes only.
>
> And on another note why not use a different delivery method altogether
> such as using java-script to encrypt the data prior to transmission,
> store the private key inside the local network, use the public key and
> associate it with the purchase within the database and develop a method
> of authentication for the users to retrieve the data and then, and only
> then use the private key to decrypt the data.
>
My apologies, I just noticed you did mention a database for storage. SSL
would probably work better then java-script. Hell if you really wanted
to secure the data prior to transmission using flash might help obscure
the data and give you a good method of assigning a unique private/public
key as well as passing it through your cipher prior to transmission.
The kerberos authentication protocol does a similar method prior to
sending the data which is always the most secure against eves droppers
and man in the middle attacks.
> Just a thought.
>
>
>
>
>
>
>
- --
Jason Gerfen
"I practice my religion
while stepping on your
toes..."
~The Ditty Bops
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFHaUaP5vk8bwKVAaIRAraBAKCFl/kkFJ9DCB4e3xF/MrOTQBxHbwCeP0mr
qMzidRdX+HQOsivTl83o9Lo=
=txPL
-----END PGP SIGNATURE-----
| |
| Jason Gerfen 2007-12-26, 7:59 am |
| -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I got messaged off list which I don't appreciate.
But, yes PHP5 only or you could replace the lines for PHP4 and on:
$keys[] = mhash( MHASH_SHA1, sha1( $array[$x] ) );
With:
if( !function_exists( mhash ) ) {
$keys[] = sha1( sha1( $array[$x] ) );
} elseif( !function_exists( sha1 ) ) {
$keys[] = md5( md5( $array[$x] ) );
} else {
$keys[] = mhash( MHASH_SHA1, sha1( $array[$x] ) );
}
That will look to see if the 'mhash()', 'sha1()' functions exist and use
them accordingly. HTH.
Jason Gerfen wrote:
> Jason Gerfen wrote:
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> My apologies, I just noticed you did mention a database for storage. SSL
> would probably work better then java-script. Hell if you really wanted
> to secure the data prior to transmission using flash might help obscure
> the data and give you a good method of assigning a unique private/public
> key as well as passing it through your cipher prior to transmission.
>
> The kerberos authentication protocol does a similar method prior to
> sending the data which is always the most secure against eves droppers
> and man in the middle attacks.
>
>
>
>
>
- --
Jason Gerfen
"I practice my religion
while stepping on your
toes..."
~The Ditty Bops
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFHcknc5vk8bwKVAaIRAmftAJ9OkHLIO43D
YZTctipo1IStY3CQJQCggPK5
Zywo2/gs5P9o7TXeYoukez0=
=fNME
-----END PGP SIGNATURE-----
| |
| Jason Gerfen 2007-12-26, 7:00 pm |
| -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
What I wrote there will work but I would highly recommend recompiling
PHP with the --with-mcrypt --with-mhash switches. The mcrypt libraries
can be found on sourceforge. http://libmcrypt.sourceforge.net
Jason Gerfen wrote:
> I got messaged off list which I don't appreciate.
>
> But, yes PHP5 only or you could replace the lines for PHP4 and on:
>
> $keys[] = mhash( MHASH_SHA1, sha1( $array[$x] ) );
>
> With:
>
> if( !function_exists( mhash ) ) {
> $keys[] = sha1( sha1( $array[$x] ) );
> } elseif( !function_exists( sha1 ) ) {
> $keys[] = md5( md5( $array[$x] ) );
> } else {
> $keys[] = mhash( MHASH_SHA1, sha1( $array[$x] ) );
> }
>
> That will look to see if the 'mhash()', 'sha1()' functions exist and use
> them accordingly. HTH.
>
> Jason Gerfen wrote:
>
>
>
>
>
>
- --
Jason Gerfen
"I practice my religion
while stepping on your
toes..."
~The Ditty Bops
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFHclgb5vk8bwKVAaIRAtd5AJ9d4hsO/In8Fdr8uRN/mq7KhHtrpACfYqAK
Lyi5IqnvtfrTsBT2WQvqgL0=
=ge1D
-----END PGP SIGNATURE-----
|
|
|
|
|