Page 1 of 3

Advanced security questions

Posted: Mon Jan 05, 2009 5:41 pm
by examancer
I am working on a secure note-taking web application. My goal is to make it the most secure note-taking web app available (I don't see a ton of competition, so I think this is attainable).

Here is where I'm at thus far: http://examancer.com/exanotes/

I have developed this without aid, so some of the assumptions I have made regarding security may not be accurate or realistic. I was hoping some of the smart people here at DevNetwork can read through the following security concepts I am relying on and tell me if they feel as confident in them as I do.

I wanted very strong anonymity. To achieve this I hash and salt both the username and password before storing them in the database. A "user" on the site is not defined by a unique username... instead it is defined as a unique username/password combination (thus more than one user named "joe" can exist if they have different passwords). Plain-text usernames and passwords are never stored anywhere, but they are kept in-memory during the session. I use standard php sessions.

Most importantly I wanted strong encryption, and I wanted it to work in such a way that even someone with direct access to the database and code base would not be able to read the notes stored by site users.

To encrypt notes I take the plain-text password available in the session, I salt it (using a different salt then the one used for salting usernames and passwords for the 'user' table), hash it, and then use it as the encryption key to encrypt and decrypt notes to and from the database (using MySQL's built-in 256-bit AES_ENCRYPT() & AES_DECRYPT() functions). Once the user session is destroyed the password is no longer available so this encryption key cannot be generated. The encryption/decryption process does add latency compared to normal SQL, especially when doing full text searching, but the performance is still very speedy with a sane number of users.

To implement the "remember me" feature for users who wish to stay logged in for an arbitrary length of time (currently 90 days) I once again use the plain-text username and password stored in the session, encrypt them using PHP's mycrypt and the RIJNDAEL_256 cipher, and then store them (as hex values) in a cookie on the user's machine. The key for this is site-wide and stored in a constant. Upon re-visiting the site the username and password in the cookie are decrypted, and the plain-text username/password is used to log the user in automatically.

I know there are some potential issues on the user end since they will be transmitting a plain-text username and password to the site through my login form. I am hoping this issue is mitigated by users who choose to access via SSL (which is available).

I also recognize that having the plain-text username and password floating around, even if only in a temporary session variable, might be a potential weak point.

Lastly, I can see that using a single site-wide key for encrypting/decrypting the username and password for "remember me" cookies is another possible weak point. However, this would probably require that an attacker have access to both the server and the client machine to exploit this.

I am looking for any commentary anyone might care to offer regarding the security for this site. I would welcome any criticism and would welcome it even more if accompanied by suggestions.

Once the tool is more polished and the security more rigid I plan to release it as an open source project, both to further "vett" the code and because of the resources required to operate the service for a large number of users (my shared hosting account probably wouldn't scale very far). I figured it would be simpler to let the paranoid people who want this kind of security run it on their own server if they wish.

If anyone would like a look at the code, or, even better, would like to help with the project, please let me know.

Thank you,
-Carl

Re: Advanced security questions

Posted: Mon Jan 05, 2009 6:12 pm
by kaisellgren
Ok. Some of my thoughts.

- In addition to salting, use also peppering.
- Isn't it easy to spam your site with infinite accounts?
- Your cipher RIJNDAEL_256 is good, but how do you generate the IV and what mode do you use? I suggest either CBC of CFB. CFB handles paddings while CBC has no information leak when using the same IV reperatious.
- SSL protects from MITMs.
- You should have this 'global' site-wide key in both in the filesystem and the database.

Re: Advanced security questions

Posted: Mon Jan 05, 2009 6:27 pm
by examancer
- Not entirely sure what peppering is :-P
- Yes, it is currently easy to spam my site with infinite accounts. What purpose would that serve though? The spammer would be the only one able to access the accounts. It wouldn't help them deliver their spam and if they are using it for a file store, there are probably better free solutions out there. However, I am keeping an eye on this and am already thinking of putting in some sort of throttle ('x' many new accounts from a single ip address every minute) and also doing something to insert random hidden data in the output to thwart people using it for storing files.
- I used either CBC or CFB... I am not sure which one I settled with since I didn't know a whole ton about them. I will check when I get home from work.
- Your confidence in SSL gives me some confidence that my design might work well :-)
- I am not sure what you mean about having the 'site-wide' key used to decrypt cookies stored in the database as well. The notes in the database are encrypted using the user's password (salted and hashed of course) as the key, so that if a key was obtained by an attacker it would limit the damage to a single user. The 'cookie key' is the only one that is 'site-wide' and is used to encrypt/decrypt user/pass values for storage on a user's machine. Having this compromised probably wouldn't be a huge deal since it should be limited to the server, and you could expire all cookies in such an event. Or are you talking about something else?

Thanks for the reply :-)

Re: Advanced security questions

Posted: Mon Jan 05, 2009 6:47 pm
by kaisellgren
examancer wrote:- Not entirely sure what peppering is :-P
- Yes, it is currently easy to spam my site with infinite accounts. What purpose would that serve though? The spammer would be the only one able to access the accounts. It wouldn't help them deliver their spam and if they are using it for a file store, there are probably better free solutions out there. However, I am keeping an eye on this and am already thinking of putting in some sort of throttle ('x' many new accounts from a single ip address every minute) and also doing something to insert random hidden data in the output to thwart people using it for storing files.
- I used either CBC or CFB... I am not sure which one I settled with since I didn't know a whole ton about them. I will check when I get home from work.
- Your confidence in SSL gives me some confidence that my design might work well :-)
- I am not sure what you mean about having the 'site-wide' key used to decrypt cookies stored in the database as well. The notes in the database are encrypted using the user's password (salted and hashed of course) as the key, so that if a key was obtained by an attacker it would limit the damage to a single user. The 'cookie key' is the only one that is 'site-wide' and is used to encrypt/decrypt user/pass values for storage on a user's machine. Having this compromised probably wouldn't be a huge deal since it should be limited to the server, and you could expire all cookies in such an event. Or are you talking about something else?

Thanks for the reply :-)
A typical case scenario is that you have a unique salt for each user in the database and a pepper in the filesystem. Then you might use them like hash(pepper.pass.salt) or so.

One account per IP sounds like a realistic idea to me. If you set no limits, anyone can greatly slowen your site down or even make it crawl.

CFB is cool if you know how to properly assign an IV. CBC is also a good choice especially the way to go if you have no knowledge on cryptography.

I mean that you should have a site-wide key in all cases. This means both in the database and in the filesystem. A hacker would need to have an access to these two instead of one in order to hack something. The something can be cookie encryption, salting user details like password, anything that involves hashing and encryption.

Re: Advanced security questions

Posted: Mon Jan 05, 2009 10:40 pm
by examancer
That is very good information. Thank you.

It turns out that I am using ECB mode to generate my iv. Is this something I should change? Here is my encryption class I wrote around it. Let me know if this needs improvement.

Code: Select all

 
class ExaCrypt {
  
  protected static $instance;
  public static $cipher = MCRYPT_RIJNDAEL_256;
  public static $mode = MCRYPT_MODE_ECB;
  protected $ivSize;
  //protected $iv;
  
  protected function __construct() {
    $this->ivSize = mcrypt_get_iv_size(self::$cipher, self::$mode);
    //$this->iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
  }
  
  public function encrypt( $text, $key ) {
    $iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
    $enc = mcrypt_encrypt(self::$cipher, $key, $text, self::$mode, $iv);
    return bin2hex( $enc );
  }
  
  public function decrypt( $enc, $key ) {
    $iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
    $text = mcrypt_decrypt(self::$cipher, $key, self::hex2bin( $enc ) , self::$mode, $iv);
    return rtrim( $text );
  }
  
  public static function getInstance() {
    if (self::$instance == null) {
      self::$instance = new ExaCrypt();
    }
    return self::$instance;
  }
  
  public static function hex2bin($h)
  {
    if (!is_string($h)) return null;
    return pack( "H*", $h );
  }
}
 

Re: Advanced security questions

Posted: Tue Jan 06, 2009 6:57 am
by kaisellgren
examancer wrote:That is very good information. Thank you.

It turns out that I am using ECB mode to generate my iv. Is this something I should change? Here is my encryption class I wrote around it. Let me know if this needs improvement.

Code: Select all

 
class ExaCrypt {
  
  protected static $instance;
  public static $cipher = MCRYPT_RIJNDAEL_256;
  public static $mode = MCRYPT_MODE_ECB;
  protected $ivSize;
  //protected $iv;
  
  protected function __construct() {
    $this->ivSize = mcrypt_get_iv_size(self::$cipher, self::$mode);
    //$this->iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
  }
  
  public function encrypt( $text, $key ) {
    $iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
    $enc = mcrypt_encrypt(self::$cipher, $key, $text, self::$mode, $iv);
    return bin2hex( $enc );
  }
  
  public function decrypt( $enc, $key ) {
    $iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
    $text = mcrypt_decrypt(self::$cipher, $key, self::hex2bin( $enc ) , self::$mode, $iv);
    return rtrim( $text );
  }
  
  public static function getInstance() {
    if (self::$instance == null) {
      self::$instance = new ExaCrypt();
    }
    return self::$instance;
  }
  
  public static function hex2bin($h)
  {
    if (!is_string($h)) return null;
    return pack( "H*", $h );
  }
}
 
By looking at the class, you set cipher and mode public and IV protected. I do not see the reason for this. The Initialization Vector is allowed to be public, It is generally sent along with the ciphertext, unencrypted. You are using IV for ECB, which makes no sense. The IV is not prepended to the plaintext before encryption. The IV is used to seed the feedback system, which is why you don't need one in ECB mode - there is no feedback.

Switch to CBC, keep RIJNDAEL since it's strong and yet fast cipher.

MCRYPT_RAND is not very random. If you are operating your project under *nix OS, then definetly use the random provided by the *nix! Which is /dev/urandom. So switch it to MCRYPT_DEV_URAND. Don't use random, use urandom. random pool may be empty at times due to its nature. urandom is more like for stable production uses. These *nix randoms are generated by "reading" device noises. Theoretically speaking, even these are not random, but serves your purpose unless you need it to be bulletproof safe from NASA or CIA ;)

Re: Advanced security questions

Posted: Tue Jan 06, 2009 11:26 am
by volomike
<deleted>

Re: Advanced security questions

Posted: Tue Jan 06, 2009 11:58 am
by kaisellgren
volomike wrote:
kaisellgren wrote:Switch to CBC, keep RIJNDAEL since it's strong and yet fast cipher.

MCRYPT_RAND is not very random. If you are operating your project under *nix OS, then definetly use the random provided by the *nix! Which is /dev/urandom. So switch it to MCRYPT_DEV_URAND. Don't use random, use urandom. random pool may be empty at times due to its nature. urandom is more like for stable production uses. These *nix randoms are generated by "reading" device noises. Theoretically speaking, even these are not random, but serves your purpose unless you need it to be bulletproof safe from NASA or CIA ;)
Kaisellgren, since you seem to really know this stuff, can you help me convert these routines I have to use MCRYPT_DEV_URAND and let me know if I'm doing a strong yet fast and compact (as possible) cipher?

Code: Select all

function Encrypt($sPrivateKey, &$vData) {
if (extension_loaded('mcrypt')) {
  $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
  $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
  $vData = mcrypt_encrypt(MCRYPT_RIJNDAEL_256,$sPrivateKey,$vData,MCRYPT_MODE_ECB,$iv);
} else {
//I have an alternate scheme here -- irrelevant
}
return $vData
}
 
function Decrypt($sPrivateKey, &$vData) {
if (extension_loaded('mcrypt')) {
  $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
  $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
  $vData = mcrypt_decrypt(MCRYPT_RIJNDAEL_256,$sPrivateKey,$vData,MCRYPT_MODE_ECB, $iv);
} else {
// alternate scheme
}
return $vData
}
You should read what I mentioned earlier and I don't think this topic is the place for you to write your own questions -- shouldn't you strike up a new thread? This topic is for discussion about examancer's website and its security.

Re: Advanced security questions

Posted: Tue Jan 06, 2009 3:20 pm
by examancer
Thanks for the info. This is great advice :-)

I had the instance variables protected because I saw no reason that a consumer of this class would need to access that info. I don't know why I left the static variables public since there is really no reason for a consumer to access those either.

Here is the revised class and example usage. Does this look like it should be secure? This class is currently used only for encrypting usernames and passwords for storage in "remember me" cookies on the client's machine.

Code: Select all

 
class ExaCrypt {
  
  protected static $instance;
  protected static $cipher = MCRYPT_RIJNDAEL_256;
  protected static $mode = MCRYPT_MODE_CBC;
  protected $ivSize;
  
  protected function __construct() {
    $this->ivSize = mcrypt_get_iv_size(self::$cipher, self::$mode);
  }
  
  public function encrypt( $text, $key ) {
    $iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
    $enc = mcrypt_encrypt(self::$cipher, $key, $text, self::$mode, $iv);
    return bin2hex( $enc );
  }
  
  public function decrypt( $enc, $key ) {
    $iv = mcrypt_create_iv($this->ivSize, MCRYPT_RAND);
    $text = mcrypt_decrypt(self::$cipher, $key, self::hex2bin( $enc ) , self::$mode, $iv);
    return rtrim( $text );
  }
  
  public static function getInstance() {
    if (self::$instance == null) {
      self::$instance = new ExaCrypt();
    }
    return self::$instance;
  }
  
  public static function hex2bin($h)
  {
    if (!is_string($h)) return null;
    return pack( "H*", $h );
  }
}
 
$text = "Encryption rules!";
// sha1, sha256 and others create too large of keys for this encryption method
$key = md5("super secret key + salt and pepper");
$enc = ExaCrypt::getInstance()->encrypt($text, $key);
echo "$text\n$key\n$enc\n\n";
echo ExaCrypt::getInstance()->decrypt($enc, $key);
 

Re: Advanced security questions

Posted: Tue Jan 06, 2009 3:23 pm
by examancer
Oh, about MCRYPT_RAND... I do need to change this at least some, as I am currently not even seeding it and it needs to be seeded. I am hoping to make the application fairly platform independent so although I would prefer to use MCRYPT_DEV_URAND, I don't know if this is realistic. I suppose I can probably use it conditionally when it is present on the system. Thanks for pointing this out.

Re: Advanced security questions

Posted: Tue Jan 06, 2009 3:27 pm
by Syntac
Hey, about the plain-text login credentials. There's a tutorial floating around here somewhere that shows you how to implement a challenge-response authentication process (i.e. no actual password is sent).

Re: Advanced security questions

Posted: Tue Jan 06, 2009 5:29 pm
by examancer
Interesting. If you find it let me know. Otherwise I will look for it when I get home from work. Thanks.

Re: Advanced security questions

Posted: Tue Jan 06, 2009 6:29 pm
by Apollo
The reason your earlier code with ECB block mode worked, even though you were using random (different) IVs for encrypting and decrypting, is (as kaisellgren already pointed out shortly) that ECB doesn't use IV at all.

For CBC mode -which I would highly recommend- you do need the IV, and then it needs to be the same when encrypting and decrypting.

One thing you can do (someone else on this forum pointed this out to me in the past, by the way) is to create a random IV every time you encrypt something, and then store that IV along with the encrypted data. And whenever decrypting something, you take the unique, random IV that was stored with the data.

However, note that the IVs do not involve any security risk, as long as your encryption keys are safe. so MCRYPT_RAND is just fine for this purpose.

Re: Advanced security questions

Posted: Tue Jan 06, 2009 7:04 pm
by examancer
Wow, you are right. I didn't realize I would need to maintain the IV each time I encrypted something. Do you think the IV would be safe to store in the user's cookie along with their encrypted user/pass? Based on statements that the IV need not be kept secret it looks like the answer is yes, but I want to make sure there aren't other potential problems with this.

If the iv is stored in the clear with the encrypted data, does CBC mode still have advantages over ECB?

Re: Advanced security questions

Posted: Tue Jan 06, 2009 7:16 pm
by Apollo
examancer wrote:Wow, you are right. I didn't realize I would need to maintain the IV each time I encrypted something. Do you think the IV would be safe to store in the user's cookie along with their encrypted user/pass? Based on statements that the IV need not be kept secret it looks like the answer is yes, but I want to make sure there aren't other potential problems with this.
It would be safe, yes, but depending on the exact working of your application it may not be suitable. Remember, cookies can be deleted by the user (even accidentally), which means the IV might suddenly be gone, and thus any data that was encrypted with it cannot be decrypted anymore.

An alternative could be to store a unique random IV in each user's profile in your database. Then one user-specific IV is used to encrypt (and decrypt) all data for that single user. Obviously you should create the IV for a user once, and never change it, otherwise his old data becomes inaccessible.
This is more or less the same as the pepper idea (the "user-specific salt").
If the iv is stored in the clear with the encrypted data, does CBC mode still have advantages over ECB?
Yes. Storing the IV in the clear does not reduce CBC's safety.
ECB is a weaker encryption, and much easier to hack, especially when structured data (text, bitmap images, etc) is encrypted.