Page 1 of 1

Securing Hippa (ePHI) data

Posted: Sat Feb 13, 2010 11:07 am
by rufio1717
Problem: I have a client (small physicians office) that wants to allow patients to make/cancel appointments request prescription refills and view lab results. The guidelines for hipaa are sparse and can be viewed at http://www.hhs.gov/ocr/privacy/

Givens: My client is hosted on InMotions VPS. The full audit trails that are require to meet HIPAA guidelines is taken care using MySQL triggers.

Methods: OpenSSL, PHP Mcrypt Extension, SSL conenction

Solution:
1. A client signs up and inputs all their personal identifiable information (ePHI). The input is sterilized using mysql_real_escape_string() and validated on the server side. The patients ePHI is sent to the server using an SSL connection and sent using $_POST.
3. The ePHI data is encrypted using symmetric rijndael-256
4. The AES-256 bit encrypted data is encrypted using OpenSSL with the public key
5. The ePHI data that has been encrypted using first rijndael-256 encrypting and then the OpenSSL public key is store in a BLOB datatype column in a MySQL database.
6. The private key is store encrypted in a non-web root directory.
7. The encrypted ePHI can only be access by the office administrators. The office administrators logs in using their username and password. They then entered a pass phrase (example: The quick brown fox jumps over the lazy dog), which decrypts the hashed OpenSSL key. The ephi is then decrypted using the private OpenSSL key and the decrypted using the symmetric rijndael-256 bit encryption.

Code: Select all

 
//sample MySQL database table 
CREATE TABLE `patients`(
userid NOT NULL auto-increment PRIMARY KEY BIGINT(20),
telephone BLOB,
first_name BLOB,
last_name BLOB,
address BLOB); //BLOB is needed because the encrypted data need to be stored in binary unless base64_encoding is used, which I am against
 
//PHP code
<?php 
/* 
 * phpFreaksCrypto.class.php5 -> phpFreaksCrypto Class (PHP5) 
 */ 
 
 /** 
  * @author Dustin Whittle 
  * @version 0.01 
  */
 
if (isset($_SERVER['HTTPS']) )
{
    echo "SECURE: This page is being accessed through a secure connection.<br><br>";
}
else
{
    echo "UNSECURE: This page is being access through an unsecure connection.<br><br>";
}
 
class phpFreaksCrypto 
{ 
   
  private $td; 
     
  // this gets called when class is instantiated 
  public function __construct($key = 'tHsd2134SDgj12SDFiq3547SDFnsdk3298kl3', $iv = false, $algorithm = 'rijndael-256', $mode = 'ecb') 
  {
    if(extension_loaded('mcrypt') === FALSE) 
    { 
      $prefix = (PHP_SHLIB_SUFFIX == 'dll') ? 'php_' : ''; 
      dl($prefix . 'mcrypt.' . PHP_SHLIB_SUFFIX) or die('The Mcrypt module could not be loaded.'); 
    } 
 
    if($mode != 'ecb' && $iv === false) 
    { 
      /* 
        the iv must remain the same from encryption to decryption and is usually 
        passed into the encrypted string in some form, but not always. 
      */ 
      die('In order to use encryption modes other then ecb, you must specify a unique and consistent initialization vector.'); 
    } 
 
    // set mcrypt mode and cipher 
    $this->td = mcrypt_module_open($algorithm, '', $mode, '') ; 
 
    // Unix has better pseudo random number generator then mcrypt, so if it is available lets use it! 
    $random_seed = strstr(PHP_OS, "WIN") ? MCRYPT_RAND : MCRYPT_DEV_RANDOM; 
 
    // if initialization vector set in constructor use it else, generate from random seed 
    $iv = ($iv === false) ? mcrypt_create_iv(mcrypt_enc_get_iv_size($this->td), $random_seed) : substr($iv, 0, mcrypt_enc_get_iv_size($this->td)); 
 
    // get the expected key size based on mode and cipher 
    $expected_key_size = mcrypt_enc_get_key_size($this->td); 
 
    // we dont need to know the real key, we just need to be able to confirm a hashed version 
    $key = substr(hash('sha512',$key), 0, $expected_key_size); 
    
    // initialize mcrypt library with mode/cipher, encryption key, and random initialization vector 
    mcrypt_generic_init($this->td, $key, $iv); 
  } 
   
  public function encrypt($plain_string)
  {     
    /* 
      encrypt string using mcrypt and then encode any special characters 
      and then return the encrypted string 
    */ 
    return base64_encode(mcrypt_generic($this->td, $plain_string)); 
  } 
   
  public function decrypt($encrypted_string) 
  {  
    /* 
      remove any special characters then decrypt string using mcrypt and then trim null padding 
      and then finally return the encrypted string 
    */ 
    return trim(mdecrypt_generic($this->td, base64_decode($encrypted_string))); 
  }   
  // this function gets called when php garbage collection destroys the object 
  public function __destruct() 
  { 
    // shutdown mcrypt 
    mcrypt_generic_deinit($this->td); 
 
    // close mcrypt cipher module 
    mcrypt_module_close($this->td); 
  } 
} 
 
$crypto = new phpFreaksCrypto(); 
$string = 'blah.blah.blah'; 
$string2 = 'foo 124 !@$# foo  foo';
$encrypted_string = $crypto->encrypt($string); 
$decrypted_string = $crypto->decrypt($encrypted_string);
 
echo 'Original: ' . $string . '<br />';
echo 'Encrypted: ' . $encrypted_string . '<br />'; 
echo 'Decrypted: ' . $decrypted_string . '<br />'; 
 
//--------
//THIS IS ONLY DONE ONCE TO GENERATE THE PUBLIC AND PRIVATE KEY PAIR
// Create the keypair
$res=openssl_pkey_new();
 
// Get private key
//this private key will then be encrypted with rijndael-256 using a pass phrase like The quick brown fox jumps over the lazy dog
//the admins can access the private key by typing in the  pass phrase an unlocking the private key
openssl_pkey_export($res, $privatekey);
 
 
// Get public key
$publickey=openssl_pkey_get_details($res);
$publickey=$publickey["key"];
//---------
 
echo "Private Key: $privatekey<br><br>Public Key:<BR>$publickey<BR><BR>";
 
$cleartext = $encrypted_string;
 
echo "Clear text: $cleartext<BR><BR>";
 
openssl_public_encrypt($cleartext, $crypttext, $publickey);
 
echo "Crypt text: $crypttext<BR><BR>";
 
openssl_private_decrypt($crypttext, $decrypted, $privatekey);
echo "OpenSSL decrypted text: $decrypted<br><br>";
$decrypted_string = $crypto->decrypt($decrypted);
echo "AES decrypted text: $decrypted_string<br><br>";
 
?>
 

Re: Securing Hippa (ePHI) data

Posted: Fri May 07, 2010 10:02 pm
by yacahuma
did you check if you actually have to do all that?

http://www.cms.gov/HIPAAGenInfo/Downloa ... charts.pdf

Re: Securing Hippa (ePHI) data

Posted: Sun May 30, 2010 3:45 pm
by yacahuma
Hello,

Can you be more specific as to where did you get the specifications (like fulll audit trails??)

Thank you

Re: Securing Hippa (ePHI) data

Posted: Tue Jun 29, 2010 11:07 pm
by rufio1717
Sorry for the late reply. The documentation is well it sucks. If you read through all the links at:
http://www.hhs.gov/ocr/privacy/hipaa/ad ... dance.html You will see that full audit trail are required??...lol. If that answers your questions. Basically the best things to do are: record all logins/login attemps(including IP timestamp etc), Limit number of false login attemps, Automatic logoff after 20 min., Have user task defined access control, use either PITA or table replication to record all edits and deletes, and force strong passwords.

Re: Securing Hippa (ePHI) data

Posted: Thu Aug 19, 2010 7:46 pm
by mecha_godzilla
Hi,

Sorry I'm a bit late joining this discussion... :)

rufio1717 - I'm trying to implement a system similar to yours and came to the same conclusions about how to encrypt the patient data, namely:

1. encrypt the patient data with a symmetric key
2. encrypt the symmetric key with a public/private key pair
3. encrypt the private key with a passphrase

Are you still going with this approach, or are you doing anything differently now? The only issue I can see that makes this approach problematic is how to store the passphrase securely for the duration of the session; all the data in my application is encrypted and the passphrase has to be available pretty much every time the user does something. Also, in your application what happens if the user forgets their login password or decryption password? You can obviously generate a new login password if necessary but how would this work for the decryption password?

Thanks in advance,

Mecha Godzilla