Thursday 15 November 2012

Overview of Security Policies for Session, Logon and Passwords.


Overview of Security Policies for Session, Logon and Passwords


<div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">I'm in the early stages of designing an ASP.NET based enterprise management system. I thought I'd shared the security policies we've tentatively settled on - partly for feedback, and partly as a point of reference for anyone else going through the same process.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">The data is business critical, accessed by a wide variety of users, variety of browsers via the web, from stations we do not control. We don’t need military or banking level security, but do need a reasonable level of security balanced against ease of use.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">Session management</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• All communications over SSL, valid SSL certificate, forced by server</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Session key stored as cookie, in format: (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> base64(binary(<domain:port>,<database_name>,<user_id>,base64(<crypto-strength random 256 bits)))</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Set cookie to "secure"</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Set cookie to "HttpOnly"</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Server forces HTTPS, with a valid SSL certificate</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Cookie to expire on browser shut down</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Cookie limited to exact domain & port</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Server checks session key against domain:port  (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Server checks session key against original IP address (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• No cross-site scripting (XSS) or user generated HTML/script in the entire application</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Server stores session key in database (sessions table)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Server bubble-caches session object in ASP.NET Cache object (unreliable but fast)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Sessions expire on explicit log off, or, after 2 hours of inactivity (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">NOTES (*):</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• We do NOT rotate session keys on a regular basis. This is complex to implement and adds little to no security for the following reasons:</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Whatever mechanism allowed the hacker to get the original session key can be used to get the new session key</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> However the server distributes the new session key could just as easily end up with the hacker instead of the victim</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Limiting session keys to a specific domain:port doesn't add much security, but seems like a good check, and is easy to implement</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Limiting session keys to a specific IP address is controversial and potentially problematic for load-balancing proxy users, but I think it will work in our situation. it's a good layer of security, and we can disable or enhance this down the track if it proves to be too much of a problem.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Expiring after 2 hours of inactivity seems a long time, but is necessary for technical reasons for our bubble-caching mechanism, and isn't unreasonable.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">Logon management</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Server only accepts salted password hashes for authentication login</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Log all login attempts, results and IP addresses in log_on_attempts table</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> </div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• For the given IP address:</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> If (IP address found in ip_addresses where black_list=1 and (black_list_expiry is null or black_list_expiry >= now)), then return "IP address blacklisted (until x)"</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> If (100 unsuccessful logins in past 24 hours) and (IP address not found in ip_addresses where white_list=1), then blacklist IP address for 24 hours. (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• For the given user name:</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> If (users.lock and users.lock_expiry_date is null or >= now), then return "user account locked (until x)"</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> If (10 unsuccessful logins in the past 24 hours), then lock the user account for 24 hours. (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• When login fails, return the reason. This is considered less secure than simply advising of logon failure, but is easier for the user to identify what they've done wrong, ie. one of the following:</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> IP address black listed (until x)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> User account locked (until x)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Invalid user name</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Invalid password</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Invalid database</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Disabled user account.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• If login unsuccessful (for any reason), pause (unsuccessful attempts in last 24 hours from this IP address) + (unsuccessful attempts in the last 24 hours for this user account) seconds before returning</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• If login unsuccessful (for invalid user name), suggest "Resend your login details?"</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• If login unsuccessful (for invalid password), suggest "Reset your password?"</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• If login unsuccessful (for disabled user account), suggest contacting head office.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">NOTES (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Setting the maximum invalid logon attempts per IP address to 100 is high, but reduces the chances of blacklisting a legitimate IP address in cases of shared office or proxy. It's extremely unlikely that our medium security passwords will be cracked in < 100 attempts.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Locking user accounts after 10 unsuccessful attempts allows a denial of service attack against a user. This is unavoidable, but unlikely, and the consequences are acceptable.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• General note - we do NOT use CAPTCHA technologies to verified users. This is hugely complicated to implement (accessibility issues, implementing audio equivalents, generating graphics automatically, conditionally verifying them in login attempts), and they're almost universally a pain in the backside for users (especially these days, when many CAPTCHA images are hard even for users to interpret, let along robots). The security CAPTCHA technologies add is unnecessary considering our user/ip address black listing policies and logon failure slow-down approach.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">Password management</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Passwords in the database are hashed as exact and lower case (*), including lower(user_name) and a 32-bit salt value:</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> "{SHA256}"+hex(sha256(binary(lower(user_name) + password) + 32_bit_salt))</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> "{SHA256}"+hex(sha256(binary(lower(user_name) + lower(password)) + 32_bit_salt))</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• New passwords must meet the following criteria:</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> 7+ characters</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Can't be cracked via on-the-fly dictionary attack, including reversed, and including suffixes "1-99"</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Can't contain strings of our company name or system name</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Contain at least 3 numbers and 3 letters</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Text portions of password 3+ characters long must not be contained in the user name (eg. user name "bob.smith" password "bob123")</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Text portions of user name (split by " ", "." and "_") 3+ characters long must not be contained in password (eg. user name "bob.smith" password "smithy272")</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Randomly generated passwords are:</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> 8 characters long</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Lower case</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Mix of letters and numbers</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Exclude characters "1,0,o,j,i,l"</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Passwords can be reset via SMS, with the following process: (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Verify the user name</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Verify it has a mobile number</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Generate new password</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> TXT the new password to the mobile</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> If an email address is associated with the user, email a notification (but not the password)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">NOTES (*)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Storing the hash of lower(password) allows us to make passwords case insensitive. This is less secure, but reasonable in our environment for several reasons:</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> The vast majority passwords are already in lower case, upper case or proper case anyway (rather than complex mixed case)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> The individuals conscious of and desiring higher password strength and still increase strength using non-alphanumeric characters</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> Password strength is enforced through other measures (length, simulated dictionary attacks, mixing alpha/numeric, compare to user name)</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"> And the benefit: We don't have to deal with support calls for caps lock or forgotten case sensitivity</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• Storing the exact hash of password as well as lower(password) allows us to support authentication for external applications which ARE case sensitive, down the track.</div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste"></div> <div style="position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;" id="_mcePaste">• General note - we don't have "secret question" style password resets. Password resets via TXT are simpler and reasonably secure.</div>
I'm in the early stages of designing an ASP.NET based enterprise management system. I thought I'd shared the security policies we've tentatively settled on after a fair amount of research - partly for feedback, and partly as a point of reference for anyone else going through the same process.
The data is business critical, accessed by ~3000 users (100,000+ down the track), from PCs we do not control. We realistically aren't after military or banking level security, but we do need a reasonable level of security, balanced against ease of use.
-Brendan


Session management
All communications over SSL, valid SSL certificate, forced by server
Session key stored as cookie, in format: (*)
base64(binary(<domain:port>,<database_name>,<user_id>,base64(<crypto-strength random 256 bits)))
Set cookie to "secure"
Set cookie to "HttpOnly"
Server forces HTTPS, with a valid SSL certificate
Cookie to expire on browser shut down
Cookie limited to exact domain & port
Server checks session key against domain:port  (*)
Server checks session key against original IP address (*)
No cross-site scripting (XSS) or user generated HTML/script in the entire application
Server stores session key in database (sessions table)
Server bubble-caches session object in ASP.NET Cache object (unreliable but fast)
Sessions expire on explicit log off, or, after 2 hours of inactivity (*)


NOTES (*):
We do NOT rotate session keys on a regular basis. This is complex to implement and adds little to no security for the following reasons:
Whatever mechanism gave the hacker the original session key can be used to get the new session key
However the server distributes the new session key could end up with the hacker instead of the victim
Limiting session keys to a specific domain:port doesn't add much security, but seems like a good check, and is easy to implement
Limiting session keys to a specific IP address is controversial and potentially problematic for load-balancing proxy users, but I think it will work in our situation. it's a good layer of security, and we can disable or enhance this down the track if it proves to be too much of a problem.
Expiring after 2 hours of inactivity seems a long time, but is necessary for technical reasons for our bubble-caching mechanism, and isn't unreasonable.


Logon management
Server only accepts salted password hashes for authentication login
Log all login attempts, results and IP addresses in log_on_attempts table
For the given IP address:
If (IP address found in ip_addresses where black_list=1 and (black_list_expiry is null or black_list_expiry >= now)), then return "IP address blacklisted (until x)"
If (100 unsuccessful logins in past 24 hours) and (IP address not found in ip_addresses where white_list=1), then blacklist IP address for 24 hours. (*)
For the given user name:
If (users.lock and users.lock_expiry_date is null or >= now), then return "user account locked (until x)"
If (10 unsuccessful logins in the past 24 hours), then lock the user account for 24 hours. (*)
When login fails, return the reason. This is considered less secure than simply advising of logon failure, but is easier for the user to identify what they've done wrong, ie. one of the following:
IP address black listed (until x)
User account locked (until x)
Invalid user name
Invalid password
Invalid database
Disabled user account.
If login unsuccessful (for any reason), pause (unsuccessful attempts in last 24 hours from this IP address) + (unsuccessful attempts in the last 24 hours for this user account) seconds before returning
If login unsuccessful (for invalid user name), suggest "Resend your login details?"
If login unsuccessful (for invalid password), suggest "Reset your password?"
If login unsuccessful (for disabled user account), suggest contacting head office.


NOTES (*)
Setting the maximum invalid logon attempts per IP address to 100 is high, but reduces the chances of blacklisting a legitimate IP address in cases of shared office or proxy. It's extremely unlikely that our medium security passwords will be cracked in < 100 attempts.
Locking user accounts after 10 unsuccessful attempts allows a denial of service attack against a user. This is unavoidable, but unlikely, and the consequences are acceptable.
General note - we do NOT use CAPTCHA technologies to verified users. This is hugely complicated to implement (accessibility issues, implementing audio equivalents, generating graphics automatically, conditionally verifying them in login attempts), and they're almost universally a pain in the backside for users (especially these days, when many CAPTCHA images are hard even for users to interpret, let along robots). The security CAPTCHA technologies add is unnecessary considering our user/ip address black listing policies and logon failure slow-down approach.


Password management
Passwords in the database are hashed as exact and lower case (*), including lower(user_name) and a 32-bit salt value:
hex(sha256(binary(lower(user_name) + password) + 32_bit_salt))
hex(sha256(binary(lower(user_name) + lower(password)) + 32_bit_salt))
New passwords must meet the following criteria:
7+ characters
Can't be cracked via on-the-fly dictionary attack, including reversed, and including suffixes "1-99"
Can't contain strings of our company name or system name
Contain at least 3 numbers and 3 letters
Text portions of password 3+ characters long must not be contained in the user name
(eg. user name "bob.smith" password "bob123")
Text portions of user name (split by " ", "." and "_") 3+ characters long must not be contained in password
(eg. user name "bob.smith" password "smithy272")
Randomly generated passwords are:
8 characters long
Lower case
Mix of letters and numbers
Exclude characters "1,0,o,j,i,l"
Passwords can be reset via SMS, with the following process: (*)
Verify the user name
Verify it has a mobile number
Generate new password
TXT the new password to the mobile
If an email address is associated with the user, email a notification (but not the password)


NOTES (*)
Storing the hash of lower(password) allows us to make passwords case insensitive. This is less secure, but reasonable in our environment for several reasons:
The vast majority of passwords are already simply in lower case, upper case or proper case anyway (rather than complex mixed case)
The individuals conscious of and desiring higher password strength and still increase strength using non-alphanumeric characters
Password strength is enforced through other measures (length, simulated dictionary attacks, mixing alpha/numeric, comparison to user name)
And the benefit: We don't have to deal with support calls for caps lock or forgotten case sensitivity
Storing the exact hash of (password) as well as lower(password) allows us to support authentication for external applications which ARE case sensitive, down the track.
General note - we don't have "secret question" style password resets. Password resets via TXT are simpler and reasonably secure.

Reference:

 http://forums.asp.net/t/1568763.aspx/1/10

No comments:

Post a Comment

Thank You for Your Comments. We will get back to you soon.

back to top