#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2013
    Location
    SA
    Posts
    6
    Rep Power
    0

    Delphi skips code


    I have written a login program, but when the program needs to check the database if the username and password are correct, it just skips the coding, and goes straight to the 'end;'.
    the coding:

    PHP Code:
    if Request 'Login' then
          begin
            AContext
    .Connection.IOHandler.ReadByte;
            
    Username := AContext.Connection.IOHandler.ReadLn(#03);
            
    AContext.Connection.IOHandler.ReadByte;
            
    Password := AContext.Connection.IOHandler.ReadLn(#03);
            
    ADOTable1.Active := True;
            
    YesNo := Check(ADOTable1UsernamePassword);
            if 
    YesNo then
              begin
                ShowMessage
    ('Correct');
                
    IdTCPClient1.Port := 81;
                
    IdTCPClient1.Connect;
                
    IdTCPClient1.IOHandler.Write(Byte($02));
                
    IdTCPClient1.IOHandler.Write('Correct Code');
                
    IdTCPClient1.IOHandler.Write(Byte($03));
                
    IdTCPClient1.Disconnect;
              
    end 
            
    else
              
    begin
                ShowMessage
    ('Incorrect');
                
    IdTCPClient1.Port := 81;
                
    IdTCPClient1.Connect;
                
    IdTCPClient1.IOHandler.Write(Byte($02));
                
    IdTCPClient1.IOHandler.Write('Incorrect Code');
                
    IdTCPClient1.IOHandler.Write(Byte($03));
                
    IdTCPClient1.Disconnect;
              
    end;
            
    ADOTable1.Active := False;
          
    end
    the function Check(ADOTable1, Username, Password) is boolean. The program skips from the "YesNo" part. I just need to know why it would just randomly skip code, and I don't receive any error messages.

    Any help would be... helpful.
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    Originally Posted by WitRot
    YesNo := Check(ADOTable1, Username, Password);
    Set a breakpoint at the line above within Delphi, and then execute just that one line. See what value YesNo has. My hunch is that your Check function doesn't return a value (there's some path through check that does not assign true nor false as the return value). You may want to post the code to Check.

    BTW: Also, as a method to sign into your application, this approach is not very secure. By using an ADOTable as opposed to an ADOQuery, you are returning every username/password from the table back to your application. Any hacker would be able to view everyone's username/password the way it's coded. Also, depending on the restrictions you have on the database itself, they may not need your application to see them at all. They simply need to open some query tool and directly query the table to view them all.
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2013
    Location
    SA
    Posts
    6
    Rep Power
    0
    Originally Posted by majlumbo
    You may want to post the code to Check.
    The code for the function is as follows:

    PHP Code:
    Function TForm1.Check(Table:TADOTable; const UsernamePassword:string):boolean;
    Begin
       Result 
    := False;
       
    Result := Table.Locate('Username:Password'VarArrayOf([UsernamePassword]), []);
    End
    Originally Posted by majlumbo
    Set a breakpoint at the line above within Delphi, and then execute just that one line. See what value YesNo has. My hunch is that your Check function doesn't return a value (there's some path through check that does not assign true nor false as the return value).
    I set the breakpoint on that line, and yet again the program didn't even pause, and the value YesNo still has nothing in it. Delphi says: "E2171 variable 'YesNo' inaccessable here due to optimizations", even when it was on the 'End;' of the program.

    Originally Posted by majlumbo
    BTW: Also, as a method to sign into your application, this approach is not very secure. By using an ADOTable as opposed to an ADOQuery, you are returning every username/password from the table back to your application.
    At first I did use an ADOQuery, but thought that an ADOTable might be easier to use here. Didn't know that it returns every username/password to the application.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    Originally Posted by WitRot
    I set the breakpoint on that line, and yet again the program didn't even pause, and the value YesNo still has nothing in it. Delphi says: "E2171 variable 'YesNo' inaccessable here due to optimizations", even when it was on the 'End;' of the program.
    That happens often, you can turn off optimization, so you can evaluate that variable during debugging.

    Follow the directions given in the link here to evaluate YesNo's Value:
    http://www.delphifaq.com/faq/delphi/delphi_ide/f591.shtml

    Make sure you don't leave assert() functions in your final product.

    as to security, there's no fool proof method, I was just pointing out every username/password is returned with a Table component (that's what Table components do). With a query you can do a query for specific user.
    Code:
    Function TForm1.Check(const Username, Password:string):boolean; 
    begin
      ADOQuery := TADOQuery.Create(nil);
      try
        ADOQuery.SQL.Text := 
      'Select count(<your table's primary key field>) as validcount from <your table name> where username := :paramname and password := :parampass';
        ADOQuery.Connection := <your connection component>;
        ADOQuery.paramByName('paramname').Value = Username;
        ADOQuery.paramByName('parampass').Value := SaltedMD5Function(Password);//see comments below
        //obviously, you'd need to write SaltedMD5Function.
        //if you choose not to salt/hash the password, just set to password.
        ADOQuery.Open;
        try
          Result := ADOQuery.FieldByName('validcount').AsInteger = 1;
        finally
          ADOQuery.Close;
        end;
      finally
        ADOQuery.Free;
      end;
    end;
    Some additional comments, in your db, you'll want to create a Unique Index on the UserName field. That would disallow the same username to be created more than once.

    Also, saving the "Actual" password in the database is also bad practice. You'll want to save possibly a hash of the password (such as MD5 hash - Already available in Delphi via Indy component), also, you may want to go one extra step, and use a "salted" MD5 hash.
    http://en.wikipedia.org/wiki/MD5
    http://en.wikipedia.org/wiki/Salt_%28cryptography%29

    So when the account is created, calculate the MD5 hash of the user's password, and save that to the table. When the user wants to log in, take the password, calculate the MD5 value apply the salt, then take the MD5 value from the DB table, apply the salt to that and compare those values.

    This still doesn't make your application fully secure, but at least puts it on the right track.
    Last edited by majlumbo; October 14th, 2013 at 01:06 PM.
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2013
    Location
    SA
    Posts
    6
    Rep Power
    0
    Originally Posted by majlumbo
    That happens often, you can turn off optimization, so you can evaluate that variable during debugging.

    Follow the directions given in the link here to evaluate YesNo's Value:
    http://www.delphifaq.com/faq/delphi/delphi_ide/f591.shtml
    The website was down when I tried looking but at least I figured out how to use the Assert() function. The variable was 'False' the entire time. I also noticed that if I put the Assert() function AFTER
    Originally Posted by WitRot
    YesNo := Check(ADOTable1, Username, Password);
    the program will still skip the 'Assert(YesNo);' part. But as I said, the variable stayed 'False' the entire time.

    Originally Posted by majlumbo
    Code:
    Function TForm1.Check(const Username, Password:string):boolean; 
    begin
      ADOQuery := TADOQuery.Create(nil);
      try
        ADOQuery.SQL.Text := 
      'Select count(<your table's primary key field>) as validcount from <your table name> where username := :paramname and password := :parampass';
        ADOQuery.Connection := <your connection component>;
        ADOQuery.paramByName('paramname').Value = Username;
        ADOQuery.paramByName('parampass').Value := SaltedMD5Function(Password);//see comments below
        //obviously, you'd need to write SaltedMD5Function.
        //if you choose not to salt/hash the password, just set to password.
        ADOQuery.Open;
        try
          Result := ADOQuery.FieldByName('validcount').AsInteger = 1;
        finally
          ADOQuery.Close;
        end;
      finally
        ADOQuery.Free;
      end;
    end;
    I tried the above code, with some modifications, and whenever I say 'ADOQuery.Open;' it skips the rest of the function, and then yet again it skips all the code and goes straight to the 'End;' of the program. even 'ADOQuery.Active := True' it skips the rest. I remember when I put my own 'ADOQuery1' onto the Form, Whenver I said 'ADOQuery1.Active := True;' It gave me an error saying 'Invalid SQL Statement'. I then went to the 'OnActivate' of the Form, and 'ADOQuery1.SQL.Clear;' and 'ADOQuery1.Active := True;', and still I received the same error.

    I also tried 'Try Except End;' on the 'ADOQuery.Open;' and the error message I receive is : "Parameter object is improperly defined. Inconsistend or incomplete information was provided".
  10. #6
  11. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    If it is skipping after the OPEN statement, then that line is causing an error. But maybe the problem is not with the SQL, but with the connection component. Check your connection string, or maybe the connection component is not connected. (ADOConnection.Connected := True; )
  12. #7
  13. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2013
    Location
    SA
    Posts
    6
    Rep Power
    0
    Originally Posted by majlumbo
    If it is skipping after the OPEN statement, then that line is causing an error. But maybe the problem is not with the SQL, but with the connection component. Check your connection string, or maybe the connection component is not connected. (ADOConnection.Connected := True; )
    Don't know why I didn't connect it. But I still receive the error: 'Parameter object is improperly defined. Inconsistent or incomplete information was given'. And that error comes from the 'ADOQuery.Open;' line. Tried setting the Datatype and Size properties, but still doesn't work..
  14. #8
  15. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    In my example code I mistakenly used Delphi's Assignment operator (:=), which should be just an equal sign (=) here since it should be SQL syntax...
    Code:
    '... := :paramname and password := :parampass '
    should be
    Code:
    '...= :paramname and password = :parampass'
    Hopefully that's what's causing the issue....
    Last edited by majlumbo; October 15th, 2013 at 11:18 AM.
  16. #9
  17. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2013
    Location
    SA
    Posts
    6
    Rep Power
    0
    Originally Posted by majlumbo
    In my example code I mistakenly used Delphi's Assignment operator (:=), which should be just an equal sign (=) here since it should be SQL syntax...
    Code:
    '... := :paramname and password := :parampass '
    should be
    Code:
    '...= :paramname and password = :parampass'
    Hopefully that's what's causing the issue....
    That's one of the issues I fixed. I first just want the program to work, then I'll worry more about security, but the code looks as follows now:

    PHP Code:
    Function TForm1.Check(const UsernamePassword:string):boolean;
    var
      
    ADOQuery :TADOQuery;
    begin

      CoInitialize
    (nil);
      
    ADOQuery := TADOQuery.Create(Self);
      
    ADOQuery.Parameters.AddParameter.Name := 'paramname';
      
    ADOQuery.Parameters.AddParameter.DataType := ftString;
      
    ADOQuery.Parameters.AddParameter.Name := 'parampass';
      
    ADOQuery.Parameters.AddParameter.DataType := ftString;

      try

        
    ADOQuery.SQL.Text := 'Select count(ID) as validcount from tblUsersDetails where Username = :paramname and Password = :parampass';
        
    ADOQuery.Connection := ADOConnection1;
        
    ADOQuery.Parameters.paramByName('paramname').Value := Username;
        
    ADOQuery.Parameters.paramByName('parampass').Value := Password;

        try
          
    ADOQuery.Open;   // here is the problem
        
    except            //it will go to this except block and give the error message I mentioned
        
    on eException do
              
    begin
                ShowMessage
    ('error: ' e.Message );  //error: Parameter object is improperly defined. Inconsistent or incomplete information was provided
              
    end;
        
    end;

        try
          
    Result := ADOQuery.FieldByName('validcount').AsInteger 1;
        
    finally
          ADOQuery
    .Close;
        
    end;
      
    finally
        ADOQuery
    .Free;
      
    end;
      
    CoUninitialize;
    end
    just a bit of extra info, I use Delphi 2010.
  18. #10
  19. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jun 2008
    Posts
    344
    Rep Power
    6
    Originally Posted by WitRot
    That's one of the issues I fixed. I first just want the program to work, then I'll worry more about security, but the code looks as follows now:

    PHP Code:
    Function TForm1.Check(const UsernamePassword:string):boolean;
    var
      
    ADOQuery :TADOQuery;
    begin

      CoInitialize
    (nil);
      
    ADOQuery := TADOQuery.Create(Self);
      
    ADOQuery.Parameters.AddParameter.Name := 'paramname';
      
    ADOQuery.Parameters.AddParameter.DataType := ftString;
      
    ADOQuery.Parameters.AddParameter.Name := 'parampass';
      
    ADOQuery.Parameters.AddParameter.DataType := ftString;

      try

        
    ADOQuery.SQL.Text := 'Select count(ID) as validcount from tblUsersDetails where Username = :paramname and Password = :parampass';
        
    ADOQuery.Connection := ADOConnection1;
        
    ADOQuery.Parameters.paramByName('paramname').Value := Username;
        
    ADOQuery.Parameters.paramByName('parampass').Value := Password;

        try
          
    ADOQuery.Open;   // here is the problem
        
    except            //it will go to this except block and give the error message I mentioned
        
    on eException do
              
    begin
                ShowMessage
    ('error: ' e.Message );  //error: Parameter object is improperly defined. Inconsistent or incomplete information was provided
              
    end;
        
    end;

        try
          
    Result := ADOQuery.FieldByName('validcount').AsInteger 1;
        
    finally
          ADOQuery
    .Close;
        
    end;
      
    finally
        ADOQuery
    .Free;
      
    end;
      
    CoUninitialize;
    end
    just a bit of extra info, I use Delphi 2010.
    I don't think you need to add these lines - not sure you can even set parameters before the SQL property is set. But, regardless, anything after the : is taken as a parameters...
    Code:
      ADOQuery.Parameters.AddParameter.Name := 'paramname';
      ADOQuery.Parameters.AddParameter.DataType := ftString;
      ADOQuery.Parameters.AddParameter.Name := 'parampass';
      ADOQuery.Parameters.AddParameter.DataType := ftString;
    Also, is ADOConnection1 already Connected before entering this code? I don't see it in the code provided.
    Last edited by majlumbo; October 15th, 2013 at 04:23 PM.
  20. #11
  21. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2013
    Location
    SA
    Posts
    6
    Rep Power
    0
    Originally Posted by majlumbo
    I don't think you need to add these lines - not sure you can even set parameters before the SQL property is set. But, regardless, anything after the : is taken as a parameters...
    Code:
      ADOQuery.Parameters.AddParameter.Name := 'paramname';
      ADOQuery.Parameters.AddParameter.DataType := ftString;
      ADOQuery.Parameters.AddParameter.Name := 'parampass';
      ADOQuery.Parameters.AddParameter.DataType := ftString;
    Also, is ADOConnection1 already Connected before entering this code? I don't see it in the code provided.
    Yes, it is. But after some playing around, I was able to fix it. Thanks for the advice, it really helped, and also for pointing me in the right direction.

IMN logo majestic logo threadwatch logo seochat tools logo