We are finishing up OWASP A3 today. Yay! I haven’t decided which section I will cover next, probably something fun like XSS or SQLi mitigations. Picking up where we left off, we are at ASVS 2.7…
ASVS 2.7 Requirement:
Verify that the strength of any authentication credentials are sufficient to withstand attacks that are typical of the threats in the deployed environment.
ASVS 2.7 Solution:
This can vary depending upon the application and type of data that needs to be protected. In any case, credentials should be hashed rather than encrypted, because a hash cannot be reversed.
A hash can be vulnerable to collision attacks (where two different values result in the same hash) or can be attacked via rainbow tables. Salting the hash with a second strong value can reduce the risk from rainbow tables. The salt should be generated with a sufficient length using a strong algorithm. In addition, the hashing function should have a work factor to increase the time it takes to generate the output, which in turn significantly increases the time it takes to brute force or generate a rainbow table. A great article on how to do this correctly can be found here.
Using a salted hash in PHP can be done like so:
// Create the salt. First generate the size, then create the salt // and encode it $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $iv = base64_encode(mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM)); // Now we hash the password with Blowfish, salted with the IV created // above, using a work factor of 12: $user_pass = base64_encode(crypt($_POST['user_pass'], "$2y$12$" . $iv));
I am using PHP’s crypt function in this example. It takes the password, combined with the algorithm and work factor ($2y is Blowfish, $12y is the “cost”) and then the salt. This operation is still fairly quick and typically something you only have to perform at login. The data in the $user_name variable would be stored in a field within the database, the salt will need to be stored as well to compare hashes, and the code above would be used to compare what is being submitted vs. the actual password for the user.
ASVS 2.8 & 2.9 Requirements:
Verify that all account management functions are at least as resistant to attack as the primary authentication mechanism.
Verify that users can safely change their credentials using a mechanism that is at least as resistant to attack as the primary authentication mechanism.
ASVS 2.8 & 2.9 Solutions:
When implementing password reset or change features, such as through the use of secret questions, hash the answers as described above. In addition, error messages should not indicate whether the questions or answers are incorrect. Instead, provide a generic failure message. Lockout an account if the there are multiple failed attempts to change/reset a password.
ASVS 2.10 Requirement:
Verify that re-authentication is required before any application-specific sensitive operations are permitted.
ASVS 2.10 Solution:
This will help prevent an attack where a user logs into the application and walks away without locking the computer or logging out of the application. This might also help prevent Cross-Site Request Forgery (CSRF) attacks. You can accomplish this with a special session variable that is required to be set and match the user’s password when performing a sensitive operation. Example:
// If the user has supplied the authentication credential, then hash and // compare $_SESSION['reauth'] = base64_encode(crypt($_POST['reauth'], "$2y$12$" . $_SESSION['salt'])); // Check whether the session variable exists, if it hasn't been set, or // if it doesn't match the password, then display the re-authentication // page. If it has been set, then present the page with the sensitive // operation: if (!isset($_SESSION['reauth'] || $_SESSION['reauth'] != $_SESSION['user_pass']) { // authentication code } else { // sensitive operation ... // Reset the authentication credential once the operation has completed $_SESSION['reauth'] = ""; }
ASVS 2.11 Requirement:
Verify that after an administratively-configurable period of time, authentication credentials expire.
ASVS 2.11 Solution:
Passwords should not last forever. This requirement can be implemented through the creation of a password date column stored with the account data in the database. Authentication code can check whether the date has exceeded the defined value and force a password change. The date can be loaded into a session variable upon login and then checked:
// Check whether the password is over 90 days old (90x24x60x60), // if so, redirect to the change password script. This would be // part of a header file for each page: if (time() - $_SESSION['Password_Age']) > 7776000) { header('Location: changePassword.php'); die(); }
Update the database value for the password age and the $_SESSION[‘Password_Age’] value once the password has been changed.
ASVS 2.12 Requirement:
Verify that all authentication decisions are logged.
ASVS 2.12 Solution:
The application should be designed such that all suspicious activity (malformed data, authentication failures, etc) is logged. Authentication decisions should be logged regardless of success or failure. You can implement PHP code to log to a file, database, SNMP, or many other mechanisms. These log files should be reviewed and correlated with other security information on a regular basis.
ASVS 2.13 Requirement:
Verify that account passwords are salted using a salt that is unique to that account (e.g., internal user ID, account creation) and hashed before storing.
ASVS 2.13 Solution:
See our solution to ASVS 2.7 above. This can be accomplished by using the username as the salt, the unique ID in the database for the account, or some other unique value tied to the account.
ASVS 2.14 Requirement:
Verify that all authentication credentials for accessing services external to the application are encrypted and stored in a protected location (not in source code).
ASVS 2.14 Solution:
Generate or purchase a private key for encrypting/decrypting a credential stored in a separate file. The private key and file should have limited permissions (read only by the application account, in Linux: chmod 400 /path/to/file). Example for decrypting:
// Read the contents of a private key into a variable $pkey = file_get_contents('/path/to/private/key'); // Read the contents of the encrypted value into a variable $cipherText = file_get_contents('/path/to/password/credential'); // Decrypt with OpenSSL using AES 128 bit algorithm $clearText = openssl_decrypt($cipherText, 'aes128', $pkey, false);
ASVS 2.15 Requirement:
Verify that all code implementing or using authentication controls is not affected by any malicious code.
ASVS 2.15 Solution:
Scan your code for viruses, or any malicious backdoors. Perform manual and automated code reviews.
Minor Bonus:
In addition to the controls listed above, make sure that you use the HTTP POST method whenever submitting credentials to a web based application. This will prevent the credentials from being stored in the browser history as part of the URL or from being stored in most web server logs (which is what can happen when using GET). Sample form code:
<form method="POST" action="script_file_goes_here.php">
We will finally move on to a new section in our next post. I hope you are excited.
Leave a Reply