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.