Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 63 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
63
Dung lượng
529,5 KB
Nội dung
Both these tasks are carried out by business tier classes that you’ll save in the business directory in the following files: password_hasher.php: Contains the PasswordHasher class, which contains the static method Hash() that returns the hash value for the password supplied. secure_card.php: Contains the SecureCard class, which represents a credit card. This class can be supplied with credit card information, which is then accessible in encrypted format. This class can also take encrypted credit card data and supply access to the decrypted information. symmetric_crypt.php: The class contained in this file, SymmetricCrypt, is used by SecureCard to encrypt and decrypt data. This means that if you ever want to change the encryption method, you only need to modify the code here, leaving the SecureCard class untouched. We’ll look at the code for hashing first, followed by encryption. Implementing Hashing Functionality in the Business Tier Hashing is a means by which you can obtain a unique value that represents an object. The algorithm used to convert the source byte array into a hashed byte array varies. The most used hashing algorithm is called MD5 (Message Digest, another name for the hash code generated), which generates a 128-bit hash value. Unfortunately, many kinds of attacks are based on word dictionaries constructed against MD5 hashes. Another popular hashing algorithm is called SHA1 (Secure Hash Algorithm), which generates a 160-bit hash value. SHA1 is generally agreed to be more secure (although slower) than MD5. In the HatShop implementation, you’ll use SHA1, although it is easy to change this if you require another type of security. Now, you’ll implement the PasswordHasher class in the follow- ing exercise. ■Note PHP doesn’t come by default with support for mhash and mcrypt, the libraries we’re using in this chapter for hashing and encryption. See Appendix A to learn how to enable support for mhash and mcrypt. Exercise: Implementing the PasswordHasher Class To implement the PasswordHasher class, follow these steps: 1. Add the following line at the end of the include/config.php file. This defines a random value (feel free to change it) to add to the passwords before hashing them. // Random value used for hashing define('HASH_PREFIX', 'K1-'); CHAPTER 11 ■ MANAGING CUSTOMER DETAILS 357 648XCH11.qxd 11/17/06 3:37 PM Page 357 2. Create a new file named password_hasher.php in the business directory, and write the PasswordHasher class in it: <?php class PasswordHasher { public static function Hash($password, $withPrefix = true) { if ($withPrefix) $hashed_password = sha1(HASH_PREFIX . $password); else $hashed_password = sha1($password); return $hashed_password; } } ?> 3. Next, write a simple test page to test the PasswordHasher class. Create a new file named test_hasher.php in the hatshop folder with the following code in it: <?php if (isset ($_GET['to_be_hashed'])) { require_once 'include/config.php'; require_once BUSINESS_DIR . 'password_hasher.php'; $original_string = $_GET['to_be_hashed']; echo 'The hash of "' . $original_string . '" is ' . PasswordHasher::Hash($original_string, false); echo '<br />'; echo ' and the hash of "' . HASH_PREFIX . $original_string . '" (secret prefix concateneted with password) is ' . PasswordHasher::Hash($original_string, true); } ?> <br /><br /> <form action="test_hasher.php"> Write your password: <input type="text" name="to_be_hashed" /><br /> <input type="submit" value="Hash it" /> </form> CHAPTER 11 ■ MANAGING CUSTOMER DETAILS358 648XCH11.qxd 11/17/06 3:37 PM Page 358 4. Load the test_hasher.php file in your favorite browser, enter a password to hash, and admire the results as shown in Figure 11-1. Figure 11-1. Testing the password hashing functionality How It Works: The Hashing Functionality The code in the PasswordHasher class is pretty simple. By default, the static Hash() method returns the hash of a string representing the secret prefix concatenated with the password. You might be wondering what the secret prefix is all about. As you might have already guessed, it has to do with security. If your database is stolen, the thief could try to match the hashed password values with a large dictionary of hashed values that looks something like this: word1 sha1(word1) word2 sha1(word2) word10000 sha1(word10000) If two hash values match, it means the original strings (which, in our case, are the customers’ passwords) also match. Appending a secret prefix to the password before hashing it reduces the risk of dictionary attacks on the hashed passwords database because the resulting string being hashed (secret prefix + password) is less likely to be found in a large dictionary of “password – hash value” pairs. The test_hasher.php page tests your newly created PasswordHasher class. CHAPTER 11 ■ MANAGING CUSTOMER DETAILS 359 648XCH11.qxd 11/17/06 3:37 PM Page 359 ■Note You can also handle hashing at the database level by using PostgreSQL cryptographic functions. First, you need to add cryptographic functions to PostgreSQL. Unix users should look in the contrib/ pgcrypto directory from PostgreSQL sources and follow the instructions (for detailed instructions please see Appendix A).Then, for example, you could execute the following PostgreSQL statement to see the PostgreSQL SHA1 in action: SELECT ENCODE(DIGEST('freedom', 'sha1'), 'hex'); Of course, when relying on PostgreSQL’s hashing functionality, the passwords travel in “plain format” to your PostgreSQL server, so if the PostgreSQL server is on another network (which is quite unlikely, however), you must secure the connection between your web server and the PostgreSQL server by using SSL connections. This can be avoided by handling hashing in the PHP code, which also offers better portability because it doesn’t rely on PostgreSQL-specific functions. Remember that for the same portability reason, we chose to use PDO instead of using PHP PostgreSQL-specific functions. Implementing the Encryption Functionality in the Business Tier Encryption comes in many shapes and sizes and continues to be a hot topic. There is no definitive solution to encrypting data, although there is plenty of advice on the subject. In general, the two forms of encryption are Symmetric encryption: A single key is used both to encrypt and decrypt data. Asymmetric encryption: Separate keys are used to encrypt and decrypt data. The encryp- tion key is commonly known as the public key, and anyone can use it to encrypt information. The decryption key is known as the private key because it can only be used to decrypt data that has been encrypted using the public key. The encryption key (public key) and the decryption key (private key) are mathematically related and are always gen- erated in pairs. The public key and private key can’t be obtained one from another. If you have a public key/private key pair, you can send the public key to parties that need to encrypt information for you. You will be the only one who knows the private key associ- ated with that public key, thus the only one able to decrypt the information. Although asymmetric encryption is more secure, it also requires much more processing power. Symmetric encryption is faster but can be less secure because both the encryptor and decryptor have knowledge of a single key. With symmetric encryption, the encryptor needs to send the key to the decryptor. With Internet communications, there is often no way of ensur- ing that this key remains a secret from third parties when it is sent to the encryptor. Asymmetric encryption gets around this by using key pairs. There is never a need for the decryption key to be divulged, so it’s much more difficult for a third party to break the encryp- tion. Because it requires a lot more processing power, however, the practical method of operation is to use asymmetric encryption to exchange a symmetric key over the Internet, which is then used for symmetric encryption safe in the knowledge that this key has not been exposed to third parties. CHAPTER 11 ■ MANAGING CUSTOMER DETAILS360 648XCH11.qxd 11/17/06 3:37 PM Page 360 In the HatShop application, things are much simpler than with Internet communications. You just need to encrypt data for storage in the database and decrypt it again when required, so you can use a symmetric encryption algorithm. ■Note Behind the scenes, some asymmetric encryption is also going on, however, because that is the method implemented by HTTPS communication. As with hashing, several algorithms can be used for both symmetric and asymmetric encryption. PHP’s mcrypt library contains implementations of the most important symmetric algorithms. No library in PHP deals with asymmetric encryption, but if you ever need to do asymmetric encryption, you can use the PGP (Pretty Good Privacy) family of software (for more information, see http://www.pgp.com) and GnuPG (http://www.gnupg.org). Two of the more commonly used asymmetric algorithms are DSA (Digital Signature Algorithm) and RSA (Rivest-Shamir-Adleman, from the names of its inventors, Ronald Rivest, Adi Shamir, and Leonard Adleman). Of these, DSA can only be used to “sign” data so that its authenticity can be verified, whereas RSA is more versatile (although slower than DSA when used to generate digital signatures). DSA is the current standard for digital authentication used by the U.S. government. Both the DSA and the RSA asymmetric algorithms are imple- mented in the PGP family of software (PGP and GnuPG). Some popular symmetric algorithms found in the mcrypt library are DES (Data Encryp- tion Standard), Triple DES (3DES), RC2 (Ron’s Code, or Rivest’s Cipher, depending on who you ask, also from Ronald Rivest), and Rijndael (from the names of its inventors, Joan Daemen and Vincent Rijmen). DES AND RIJNDAEL DES has been the standard for some time now, although this is gradually changing. It uses a 64-bit key, how- ever, in practice only 56 of these bits are used (8 bits are “parity” bits), which are not strong enough to avoid being broken using today’s computers. Both Triple DES and RC2 are variations of DES. Triple DES effectively encrypts data using three separate DES encryptions with three keys totaling 168 bits when parity bits are subtracted. The RC2 variant can have key lengths up to 128 bits (longer keys are also possible using RC3, RC4, and so on), so it can be made weaker or stronger than DES depending on the key size. Rijndael is a completely separate encryption method and has now been accepted as the new AES (Advanced Encryption Standard) standard (several competing algorithms were considered before Rijndael was chosen). This standard is intended to replace DES and is gradually becoming the most used (and secure) symmetric encryption algorithm. The tasks associated with encrypting and decrypting data are a little more involved than hashing. The mcrypt functions are optimized to work with raw data, so you have some work to do with data conversion. You also have to define both a key and an initialization vector (IV) to perform encryption and decryption. The IV is required due to the nature of encryption: the CHAPTER 11 ■ MANAGING CUSTOMER DETAILS 361 648XCH11.qxd 11/17/06 3:37 PM Page 361 data blocks are usually encrypted in sequence, and calculating the encrypted values for one sequence of bits involves using some data from the preceding sequence of bits. Because there are no such values at the start of encryption, an IV is used instead. For AES encryption (Rijndael_128), the IV and the key must be 32 bytes long. ■Note At http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation, you can learn more about the various modes of encryption. The general steps required for encrypting a string are as follows: 1. Create a 32-byte random IV. 2. Convert the IV (which you keep as a hexadecimal string) into a byte array. 3. Encrypt the string using AES encryption by supplying the IV in byte array format. 4. Convert the resulting encrypted data from a byte array into a hexadecimal string. Decryption follows a similar scheme: 1. Convert the IV (which you keep as a hexadecimal string) into a byte array (the same with the encryption first step). 2. Convert the string to decrypt into a byte array. 3. Decrypt the binary string from the previous step by supplying the IV in a byte array. In your code, you’ll use AES, but the code in the SymmetricCrypt class can be modified to use any of the supported encryption algorithms. Exercise: Implementing the SymmetricCrypt Class 1. Add a new file in the business directory called symmetric_crypt.php with the following code in it: <?php class SymmetricCrypt { // Encryption/decryption key private static $_msSecretKey = 'From Dusk Till Dawn'; // The initialization vector private static $_msHexaIv = 'c7098adc8d6128b5d4b4f7b2fe7f7f05'; // Use the Rijndael Encryption Algorithm private static $_msCipherAlgorithm = MCRYPT_RIJNDAEL_128; /* Function encrypts plain-text string received as parameter and returns the result in hexadecimal format */ CHAPTER 11 ■ MANAGING CUSTOMER DETAILS362 648XCH11.qxd 11/17/06 3:37 PM Page 362 public static function Encrypt($plainString) { // Pack SymmetricCrypt::_msHexaIv into a binary string $binary_iv = pack('H*', self::$_msHexaIv); // Encrypt $plainString $binary_encrypted_string = mcrypt_encrypt( self::$_msCipherAlgorithm, self::$_msSecretKey, $plainString, MCRYPT_MODE_CBC, $binary_iv); // Convert $binary_encrypted_string to hexadecimal format $hexa_encrypted_string = bin2hex($binary_encrypted_string); return $hexa_encrypted_string; } /* Function decrypts hexadecimal string received as parameter and returns the result in hexadecimal format */ public static function Decrypt($encryptedString) { // Pack Symmetric::_msHexaIv into a binary string $binary_iv = pack('H*', self::$_msHexaIv); // Convert string in hexadecimal to byte array $binary_encrypted_string = pack('H*', $encryptedString); // Decrypt $binary_encrypted_string $decrypted_string = mcrypt_decrypt( self::$_msCipherAlgorithm, self::$_msSecretKey, $binary_encrypted_string, MCRYPT_MODE_CBC, $binary_iv); return $decrypted_string; } } ?> 2. Add a test file in the hatshop folder called test_encryption.php with the following code: <?php if (isset ($_GET['my_string'])) { require_once 'include/config.php'; CHAPTER 11 ■ MANAGING CUSTOMER DETAILS 363 648XCH11.qxd 11/17/06 3:37 PM Page 363 require_once BUSINESS_DIR . 'symmetric_crypt.php'; $string = $_GET['my_string']; echo 'The string is:<br />' . $string . '<br /><br />'; $encrypted_string = SymmetricCrypt::Encrypt($string); echo 'Encrypted string: <br />' . $encrypted_string . '<br /><br />'; $decrypted_string = SymmetricCrypt::Decrypt($encrypted_string); echo 'Decrypted string:<br />' . $decrypted_string; } ?> <br /><br /> <form action="test_encryption.php"> Enter string to encrypt: <input type="text" name="my_string" /><br /> <input type="submit" value="Encrypt" /> </form> 3. Load the newly created test_encryption.php file in your favorite browser and give a string to encrypt/decrypt (see Figure 11-2). ■Note If the mcrypt library wasn’t installed or configured correctly, you’ll receive a fatal error about the call to mcrypt_encrypt(). If that happens, check the installation instructions in Appendix A. Figure 11-2. Testing encryption CHAPTER 11 ■ MANAGING CUSTOMER DETAILS364 648XCH11.qxd 11/17/06 3:37 PM Page 364 ■Caution As you might have noticed after running the test page, the decrypted string always has a length that is a multiple of 32 bytes. If the original string is less than 32 bytes, null characters are appended until the string’s length becomes a multiple of 32 bytes. You need to be careful with this detail because it means the decrypted value of the string may not be identical to the encrypted value. For our HatShop project, because we’ll encrypt XML data and the values of interest are between XML tags, we won’t need to worry about having additional void characters at the end of the string. How It Works: Encryption Functionality in the Business Tier The SymmetricCrypt class has two static methods, Encrypt() and Decrypt(), which encrypt and decrypt data, and a number of encryption configurations parameters stored as static members: // Encryption/decryption key private static $_msSecretKey = 'From Dusk Till Dawn'; // The initialization vector private static $_msHexaIv = 'c7098adc8d6128b5d4b4f7b2fe7f7f05'; // Use the Rijndael Encryption Algorithm private static $_msCipherAlgorithm = MCRYPT_RIJNDAEL_128; The secret key is 16 characters (bytes) long for AES algorithms. Using a smaller key is allowed by the mcrypt library but will reduce the encryption security. The IV should be exactly 16 bytes long for AES and will be kept as a hexadecimal string (2x16=32 chars long). Both $_msSecretKey and $_msHexaIv variables are set to temporary values here. They could just as easily take any other values, depending on the key you want to use. Encrypt() starts by converting the IV from its hexadecimal value to a byte array because this is the format expected by the mcrypt_encrypt function (the one that does the actual encryption): // Pack SymmetricCrypt::_msHexaIv into a binary string $binary_iv = pack('H*', self::$_msHexaIv); The conversion is done using PHP’s pack function (learn more about it at http://www.php.net/pack). The call to mcrypt_encrypt follows: // Encrypt $plainString $binary_encrypted_string = mcrypt_encrypt( self::$_msCipherAlgorithm, self::$_msSecretKey, $plainString, MCRYPT_MODE_CBC, $binary_iv); This is the call that performs the actual encryption. Its parameters are obvious, and you can find more detail about the mcrypt_encrypt function at http://www.php.net/mcrypt. The MCRYPT_MODE_CBC specifies the “cipher block chaining” encryption method; this method uses a chaining mechanism in which the encryption of CHAPTER 11 ■ MANAGING CUSTOMER DETAILS 365 648XCH11.qxd 11/17/06 3:37 PM Page 365 each block of data depends on the encryption results of preceding blocks, except for the first block in which the IV is used instead. At the end, the encrypted string is transformed into hexadecimal format, which is easier to work with (for example, to save in the database or in a configuration file): // Convert $binary_encrypted_string to hexadecimal format $hexa_encrypted_string = bin2hex($binary_encrypted_string); The Decrypt() method is very similar to the Encrypt() method. First, you need the IV to be in a binary form (the same first step you took in the Encrypt() method). As the Encrypt() method returns the encrypted string as a hexadecimal string, the input parameter of Decrypt() is also a hexadecimal string. You must convert this string to a byte array, which is the format that mcrypt_decrypt needs: // Convert string in hexadecimal to byte array $binary_encrypted_string = pack('H*', $encryptedString); // Decrypt $binary_encrypted_string $decrypted_string = mcrypt_decrypt( self::$_msCipherAlgorithm, self::$_msSecretKey, $binary_encrypted_string, MCRYPT_MODE_CBC, $binary_iv); return $decrypted_string; The test_encryption.php test file for this class simply encrypts and decrypts data, demonstrating that things are working properly. The code for this is very simple, so we won’t detail it here. Now that you have the SymmetricCrypt class code, the last step in creating the security-related classes is to add the SecureCard class. Storing Credit Cart Information Using the SecureCard Class In the following exercise, you’ll build the SecureCard class, which represents the credit card of a customer. This class will use the functionality you implemented in the previous two exer- cises to ensure that its data will be stored securely in the database. Exercise: Implementing the SecureCard Class 1. Create a new file named secure_card.php in the business folder, and add the following code to it: <?php // Represents a credit card class SecureCard { CHAPTER 11 ■ MANAGING CUSTOMER DETAILS366 648XCH11.qxd 11/17/06 3:37 PM Page 366 [...]... the customer ID for that user to be saved in the session 5 Use the query tool to execute this code, which creates the customer_get_customer function in your hatshop database: Create customer_get_customer function CREATE FUNCTION customer_get_customer(INTEGER) RETURNS customer LANGUAGE plpgsql AS $$ DECLARE inCustomerId ALIAS FOR $1; outCustomerRow customer; BEGIN SELECT INTO outCustomerRow customer_id,... 11/ 17/ 06 3: 37 PM Page 377 CHAPTER 11 ■ MANAGING CUSTOMER DETAILS Figure 11-4 HatShop with a login box The new user registration page looks like Figure 11-5 Figure 11-5 The new user registration page in HatShop 377 648XCH11.qxd 378 11/ 17/ 06 3: 37 PM Page 378 CHAPTER 11 ■ MANAGING CUSTOMER DETAILS After the user logs in to the site, a new componentized template appears on top of the departments list to. .. if ($addAndLogin) $_SESSION['hatshop_customer_id'] = $customer_id; return $customer_id; } public static function Get($customerId = null) { if (is_null($customerId)) $customerId = self::GetCurrentCustomerId(); // Build the SQL query $sql = 'SELECT * FROM customer_get_customer(:customer_id);'; // Build the parameters array $params = array (':customer_id' => $customerId); // Prepare the statement with... day_phone, eve_phone, mob_phone FROM customer WHERE customer_id = inCustomerId; RETURN outCustomerRow; END; $$; The customer_get_customer function returns full customer details for a given customer ID 6 Use the query tool to execute this code, which creates the customer_update_account function in your hatshop database: Create customer_update_account function CREATE FUNCTION customer_update_account(INTEGER,... 11 ■ MANAGING CUSTOMER DETAILS Exercise: Creating the Database Functions 1 Load pgAdmin III, and connect to the hatshop database 2 Click Tools ➤ Query tool (or click the SQL button on the toolbar) A new query window should appear 3 Use the query tool to execute this code, which creates the customer_login type and customer_get_login_info function in your hatshop database: Create customer_login_info... LANGUAGE plpgsql AS $$ DECLARE inName ALIAS FOR $1; inEmail ALIAS FOR $2; inPassword ALIAS FOR $3; outCustomerId INTEGER; BEGIN INSERT INTO customer (name, email, password) VALUES (inName, inEmail, inPassword); SELECT INTO outCustomerId currval('customer_customer_id_seq'); 648XCH11.qxd 11/ 17/ 06 3: 37 PM Page 381 CHAPTER 11 ■ MANAGING CUSTOMER DETAILS RETURN outCustomerId; END; $$; The customer_add function... type CREATE TYPE customer_login_info AS ( customer_id INTEGER, password VARCHAR(50) ); Create customer_get_login_info function CREATE FUNCTION customer_get_login_info(VARCHAR(100)) RETURNS customer_login_info LANGUAGE plpgsql AS $$ DECLARE inEmail ALIAS FOR $1; outCustomerLoginInfoRow customer_login_info; BEGIN SELECT INTO outCustomerLoginInfoRow customer_id, password FROM customer WHERE email =... inShippingRegionId WHERE customer_id = inCustomerId; END; $$; The customer_update_address function updates the customer’s address in the database Implementing the Business Tier In the business folder, create a new file named customer .php that will contain the Customer class The Customer class is a little longer, and it mainly accesses the data tier functionality to respond to requests that come from the presentation... preferred to use a new and unique feature of PHP 5 called SimpleXML Although less complex and powerful than DOMDocument, the SimpleXML extension makes parsing XML data a piece of cake by transforming it into a data structure you can simply iterate through: // Extract information from XML credit card data private function ExtractXml($decryptedData) { 373 648XCH11.qxd 374 11/ 17/ 06 3: 37 PM Page 374 CHAPTER... business/customer .php file: < ?php // Business tier class that manages customer accounts functionality class Customer { // Checks if a customer_id exists in session public static function IsAuthenticated() { if (!(isset ($_SESSION['hatshop_customer_id']))) return 0; else return 1; } // Returns customer_id and password for customer with email $email public static function GetLoginInfo($email) { // Build the SQL . 359 648XCH11.qxd 11/ 17/ 06 3: 37 PM Page 359 ■Note You can also handle hashing at the database level by using PostgreSQL cryptographic functions. First, you need to add cryptographic functions to PostgreSQL. Unix. contrib/ pgcrypto directory from PostgreSQL sources and follow the instructions (for detailed instructions please see Appendix A).Then, for example, you could execute the following PostgreSQL statement to. format” to your PostgreSQL server, so if the PostgreSQL server is on another network (which is quite unlikely, however), you must secure the connection between your web server and the PostgreSQL