0

I am working on a legacy ASP.NET website project that utilizes a custom MembershipProvider. I want to take advantage of the existing functionality that this provides to implement password validation. I want to ensure that all future passwords are of a certain length and contain a certain number of uppercase, lowercase, and non-alphanumeric characters.

What I have done so far is register a MembershipValidatePasswordEventHandler in the page_load function of the default page of the website based on the example from the docs, here. I currently have the event handler set up to reject everything just as a proof of concept, and it works. Despite that, it seems like an odd place to register the event handler considering there are multiple pages in the site, and the default page doesn't have anything to do with password creation/management.

public partial class _Default : System.Web.UI.Page
{
     protected void Page_Load(object sender, EventArgs e)
     {
          Membership.ValidatingPassword += new MembershipValidatePasswordEventHandler(ValidatePasswordEventHandler);
     }
     ...
     public void ValidatePasswordEventHandler(object sender, ValidatePasswordEventArgs e)
     {
          e.Cancel = true;
     }
}

I have considered registering the handler in the user creation or updating pages, but that still seems inappropriate. I would like to know if there is a more appropriate place to do this before I begin implementing the actual password checks, or is this the standard place to do this?

For additional context on the broader problem, I have also tried modifying the membership provider in the web.config file to get part of this functionality based on this previous answer, but when I provide an invalid password (smaller than 7 characters) it isn't rejected.

<membership defaultProvider="MyMembershipProvider" userIsOnlineTimeWindow="15">
  <providers>
    <add name="MyMembershipProvider" type="MyNamespace.Membership.MyMembershipProvider" applicationName="MyApp" connectionStringName="MyConnectionString" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="true" writeExceptionsToEventLog="false" passwordFormat="Clear" maxInvalidPasswordAttempts="15" passwordAttemptWindow="10" minRequiredPasswordLength="7"/>
  </providers>
</membership>

Update: This question may also provide some additional justification for why I am interested in the is event handler. The top answer links to this question. The top answer for this question mentions doing what I have done here, but says it might be done "in a higher scope" this is more getting at my question. What would a higher/highest scope be in the context of a website project?

Solution: I ended up modifying my ChangePassword web control's NewPasswordRegularExpression field with the following regex: ^(?=.[a-z])(?=.[A-Z])(?=.\d)(?=.[^\da-zA-Z]).{8,}$, instead of implementing a custom event handler.

1 Answers1

0

Hum, ok. Well, the logon page of course is specified in web.config.

and on that logon page, you most likely used a asp.net "logon" control. so that logon control will have the membership provider - say like this:

<asp:Login ID="Login1" runat="server"  Width="675px" Height="177px" 
 MembershipProvider="MySqlProvider" UserNameLabelText="Username:" 
 FailureText="Login attempt was not successful. Please try again." Font-Size="Small" 
 >

  <LayoutTemplate>
  <div style="margin-left:10px">
            <h>Please enter your Username and Password</h>
            <br />

So, some place in your application - a membership provider was created, and will (should) exist in the applcation. So the above logon control will "consume" and use that memberships provider class. And in that class, there will be a routine "somthing" like this:

Public Overrides Function ValidateUser(username As String, password As String) As Boolean

    Dim rst As DataTable

    Dim strSQL As String =
             "SELECT * from dbo_ContactName WHERE Email = @user AND PasswordHash = @pass " &
             "AND IsWebActive = 1"

    Dim cmdSQL As New SqlCommand(strSQL)
    cmdSQL.Parameters.Add("@Email", SqlDbType.NVarChar).Value = username
    cmdSQL.Parameters.Add("@pass", SqlDbType.NVarChar).Value = password

    rst = MyRstP(cmdSQL)

So, the logon page has a logon control - but it WILL wind up calling the membership provider "ValidateUser" as per above.

And so while it don't seem like there is "any" connection, or specifying of how the custom provider calls the above validate user? Well, the markup in the logon control specified that membership provider - and that's how the two are connected.

In your logon page (the page with the asp.net logon control), if everything goes well, then this event fires:

Protected Sub Login1_LoggedIn(sender As Object, e As EventArgs) Handles Login1.LoggedIn

    ' this event raises AFTER the user is authenticated
    '
    ' check if user is first time logon
    ' this means that user id = email address

     etc. etc. - do whatever you want for custom logon code.

I'm not sure if the above helps, but it at least shows how the logon control and the membership provider are connected to each other. Now, it is possible that the standard asp.net logon control was never used - and that being the case, then the event model for the logon control thus would not have been used. Displaying the property sheet for the log on control, we see this:

enter image description here

So the logon control has a set of events - but the actual validation of the user occurs inside of the custom sqlmemberShipProivder class that I'm assuming was used here. That class thus should have a whole bunch of things like GetUser, ValidateUser, ChangePassword. The whole idea here is WHEN that provider is written correctly, then use of logon control, change password control etc will all JUST work for you, without having to build up the logon control(s) provided in the toolbox for this process.

So, in that custom class (sqlMemberShipProivder), there is this: (and many more)

enter image description here

I don't see a Validate Password user - but there is a ValidateUser event.

Albert D. Kallal
  • 42,205
  • 3
  • 34
  • 51
  • Ok, so it seems that I may be lost in the weeds then by digging around in internal events? The overarching goal is to enforce password requirements, and I'll start looking down different avenues. If you have any advice, would it be reasonable then to simply define a method say, MyValidatePassword that is called by CreateUser, ChangePassword, and ResetPassword? – Kenneth Ward Dec 27 '21 at 15:39
  • Password requirements is a VAST VAST different question. You need to find that custom sqlprovider class. If there is not one present, then you using the built in class and object. Things like min password length, max password length etc will ALSO be found in that class. The whole idea is YOUR custom provider will use EXACT same methods found in the base class. They match since config and setup of things like min/max password length, complexity of password etc. are setup in IIS config panels. You break that object model and you now trying to write security system and not using IIS for this. – Albert D. Kallal Dec 27 '21 at 17:59
  • and yes, a boatload of places behind the scenes will call and use validate user. IIS services will call and use that method any old time it pleases. That ONE method has ONE use and ONE use alone - does user + password exists - yes or no? Things like password length etc. is a 100% different method - not related to the validate method. So methods for allowed number of attempts, the allowed format etc. are different methods. The first and simple question is did you find the sqlProvider class in that project? If yes, then look at that class. If not, then you NOT using a custom sqlprovider at all – Albert D. Kallal Dec 27 '21 at 18:03
  • However, the fact that you may not be using a custom sql provider does not prevent, or necessary determine other things like password policies etc. The MAIN reason why you would use a custom sqlprovider for security is because you most likely did NOT want to use the standard table(s) for security. You might have for example already had some applcation that contained customers, and you wanted to use that customer table in place of the standard asp scripts that generates a user table for you. So custom provider allows you to use a different table to contain users as opposed to default table. – Albert D. Kallal Dec 27 '21 at 18:05
  • So don't confuse the issue of a custom sql provider in place of say wanting to change or have some password policy - in 99% of cases, just adding some logon code can acheive what you want. As I stated, you have to first determine if a custom logon system (a custom sqlprovider) was created or being used here. If not then you using the built in provider for security - and that is also fine. So, don't confuse the standard asp.net logon system and security system with that of wanting to achieve some changes in say password requirements. – Albert D. Kallal Dec 27 '21 at 18:08
  • in 99% of cases you can change or write code for such requirements, and inherating the base sql security provider class is NOT required, nor recommeded. But in summary, yes the validdate user stub must have the correct name and the args etc. must match exclty the built in method for this purpose. However, if you don't have a custom sqlprovider for passwords, I would certainly not attempt to introduce one. You can almost for sure do simple things like some change in password requirements without having to build a whole replacement for the built in security provider (a custom sqlprovider). – Albert D. Kallal Dec 27 '21 at 18:11
  • Ok, I see what you mean. I do have a custom MembershipProvider class in my project (not a custom SQLMembershipProvider) and I thought that would be a good place to start since it was already present, but following your advice, it does seem unnecessary to modify it for now. There is a ChangePassword control in my project and I have simply added a [regular expression to a field of this control](https://learn.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.changepassword.newpasswordregularexpression?view=netframework-4.8) which checks new passwords against the given regex. – Kenneth Ward Dec 30 '21 at 22:37
  • This issue I see ahead is with another control made expressly for administrators to change other user's passwords. This control is from a proprietary library (DevExpress) and is a table control ([DexExpress.Web.ASPxGridView](https://docs.devexpress.com/AspNet/DevExpress.Web.ASPxGridView)). This control pulls the user information out of the underlying database with an SQLDataSource, but is not configured to perform password checks. Probably calls for a separate question at this point. – Kenneth Ward Dec 30 '21 at 22:43
  • didn't realize devExpress. My whole narrative was quite much one BETTER follow the standard providers - else one will wind up re-writing all kinds of stuff - way to much. As I pointed out, if one follows the exact same sqlProvider interface, then all of the existing asp.net controls from logon to change password, and even IIS administration tools should continue to work without changes. .So, 100% agree here. If one "breaks" the existing security model - you wind up writing your own security system - and that's a bad idea. So, a custom sqlProvider for logons? Great idea, but follow its design – Albert D. Kallal Dec 30 '21 at 22:48
  • The admin panel you make? you can probably just cook up a standard asp.net page, and have a option to change passwords - as such, you can write direct code to do this for you. As for Membership provider vs SQLMembership provider? They are the same thing, not two different things or concpets. And you are 100% correct to have used and dropped in a regex validator. As I stated, you RARE need to create your own role or membership provider. But for that admin page, to change things like active users, or add them to particular security groups (roles), you again RARE need to create custom providers. – Albert D. Kallal Dec 31 '21 at 21:09
  • You are 100% free to even add new users - and use plane jane code to add such users to the existing users table (along with child records for their security role. There not necessary the need or requirement to use built in methods - but some of them are handy for you to use, and again, in most cases such code and even a custom manager page does not require re-creating a role or sql "user" provider for this goal. – Albert D. Kallal Dec 31 '21 at 21:11
  • Right, I'm not sure why the original designers decided to build a custom MembershipProvider (I only have the source code. No documentation at all. Only some high level information from upper management), but I do know there was a pre-existing database that may have not been compatible with SQLProvider, but I will avoid messing with the MembershipProvider if I can save time with other options. For the admin page, it looks like they don't use the MembershipProvider anyway. They just connect directly to the database with an SQLDataSource. – Kenneth Ward Jan 04 '22 at 23:05
  • I'll just have to find a way to get that edit field to check against the regex, or as you say I may start from scratch. Thanks a lot! – Kenneth Ward Jan 04 '22 at 23:06
  • No, I am VERY much suggesting that you have no need to start from scratch at all. And as I stated, it doubtful that custom sql provider for membership and roles was created here. In fact, on that form with the logon control, in design mode, right click and choose "convert to template". the result will be ALL OF the markup required for that logon control - but is now all markup. You can now then just drop in a validator into that page like we do for most validations here. Have you tested or attempted to convert the logon control to a template (or maybe already done???). – Albert D. Kallal Jan 04 '22 at 23:21
  • Try this idea: create a blank new test page - drop in a logon control from the toolbox. Now, in desinger - right click, and choose convert to template. Now, go look at the markup. And also select the template (not a control inside - since we just converted to template), and display the properties sheet - note the standard events you can have for code behind now, and note the code behind events you have. I actually suggest you play and try a test page - you see you have really all the options you need here. – Albert D. Kallal Jan 04 '22 at 23:30
  • I see, so you can convert a control to a template to see all the elements that make it up and presumably customize the built in controls from there with things like the validator you mentioned earlier. Then in the properties tab there's a list of most of the events I'll ever need and I can just define methods in the code-behind to create the behavior I want when those events occur. That's incredibly useful, and I can see that there is an update event in the relevant control of the admin page. I should then be able to define a method that performs password validation in response to that. – Kenneth Ward Jan 05 '22 at 22:24
  • 100% correct. So, you really don't have to touch the sql "membership" provider, and you really don't in general have to touch the sql "role" provider - they are considrable higher level features. As I pointed out, I did wind up writing my own membership provider, but even then when I inherted the built in membership provider, then all of the code stubs appeared and were laid out for me. I only wanted to change the "user" table to a existing table that existed for a existing applcation. It was a desktop application and sql server - and we wanted to build a customer web portal based on same data – Albert D. Kallal Jan 06 '22 at 01:21
  • And for example, we have customers when activated? We create the user in code, and then set the password to same as email. However, in the above events you note? We just check if password = email name, and we direct them right over to the built in change password page - again, even that ability did not require me to mess with, or touch the built in providers. And as I stated, the reason why you don't' want to touch or mess with the providers? Because "as is", they work seamless with IIS, and you will be using IIS to setup security settings for a lot of things. – Albert D. Kallal Jan 06 '22 at 01:24