I recently worked on a project that called for a feature to expire Sitecore users’ passwords after an elapsed period of time since their passwords were last changed.
The idea behind this is to lessen the probability that an attacker will infiltrate a system — or multiple systems if users reuse their passwords across different applications (this is more common than you think) — within an organization by acquiring old database backups containing users’ passwords.
Since I can’t show you what I built for that project, I cooked up another solution — a custom loggingin processor that determines whether a user’s password has expired in Sitecore:
using System; using System.Web.Security; using Sitecore.Diagnostics; using Sitecore.Pipelines.LoggingIn; using Sitecore.Web; namespace Sitecore.Sandbox.Pipelines.LoggingIn { public class CheckPasswordExpiration { private TimeSpan TimeSpanToExpirePassword { get; set; } private string ChangePasswordPageUrl { get; set; } public void Process(LoggingInArgs args) { Assert.ArgumentNotNull(args, "args"); if (!IsEnabled()) { return; } MembershipUser user = GetMembershipUser(args); if (HasPasswordExpired(user)) { WebUtil.Redirect(ChangePasswordPageUrl); } } private bool IsEnabled() { return IsTimeSpanToExpirePasswordSet() && IsChangePasswordPageUrlSet(); } private bool IsTimeSpanToExpirePasswordSet() { return TimeSpanToExpirePassword > default(TimeSpan); } private bool IsChangePasswordPageUrlSet() { return !string.IsNullOrWhiteSpace(ChangePasswordPageUrl); } private static MembershipUser GetMembershipUser(LoggingInArgs args) { Assert.ArgumentNotNull(args, "args"); Assert.ArgumentNotNullOrEmpty(args.Username, "args.Username"); return Membership.GetUser(args.Username, false); } private bool HasPasswordExpired(MembershipUser user) { return user.LastPasswordChangedDate.Add(TimeSpanToExpirePassword) <= DateTime.Now; } } }
The processor above ascertains whether a user’s password has expired by adding a configured timespan — see the configuration file below — to the last date and time the password was changed, and if that date and time summation is in the past — this means the password should have been changed already — then we redirect the user to a change password page (this is configured to be the Change Password page in Sitecore).
I wired up the custom loggingin processor, its timespan on expiring passwords — here I am using 1 minute since I can’t wait around all day 😉 — and set the change password page to be the url of Sitecore’s Change Password page:
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <processors> <loggingin> <processor mode="on" type="Sitecore.Sandbox.Pipelines.LoggingIn.CheckPasswordExpiration, Sitecore.Sandbox" patch:before="processor[@type='Sitecore.Pipelines.LoggingIn.CheckStartPage, Sitecore.Kernel']"> <!-- Number of days, hours, minutes and seconds after the last password change date to expire passwords --> <TimeSpanToExpirePassword>00:00:01:00</TimeSpanToExpirePassword> <ChangePasswordPageUrl>/sitecore/login/changepassword.aspx</ChangePasswordPageUrl> </processor> </loggingin> </processors> </sitecore> </configuration>
Let’s test this out.
I went to Sitecore’s login page, and entered my username and password on the login form:
I clicked the Login button, and was redirected to the Change Password page as expected:
If you can think of any other security measures that should be added to Sitecore, please share in a comment.
one of the improvements that could be added as well, is to validate that the new password meets certain criteria like strength and a history trail (password cannot be re-used in let’s say the next 10 times)
Excellent ideas!
Setting the following attributes on /configuration/system.web/membership/providers/add[@name=”sql”] in the Web.config would increase password strength:
We might need to create a custom System.Web.Security.MembershipProvider to ascertain whether a password was used in the past — this could leverage a custom data-store that would keep track of previous passwords for users.
Password in database always save in encrypted format so there is already we have the security with sitecore, if you want to enforce the password should be change the use domain security for this.
Although passwords are encrypted in the ASP.NET Membership tables, a cracker could easily write a program using logic defined in GetPasswordWithFormat() and EncodePassword() defined in System.Web.Security.SqlMembershipProvider — assuming this “out of the box” MembershipProvider is being used — to “guess” passwords from a “dictionary” of words.
By establishing minimum complexity requirements, you’re going to foil any dictionary-based checking. If you are paranoid, you could check a potential password against a “dictionary” containing not only actual words, but common passwords.