-1

I have hashed passwords stored in my database and I want to implement a password change function. Before updating the password, I want to check how similar the old and new passwords are. I have used the difflib library to compare the two values, but even when the old and new passwords are the same, the function successfully updates the password without giving a warning about their similarity.

from werkzeug.security import generate_password_hash, check_password_hash
from difflib import SequenceMatcher
@accounts.route('/<int:id>', methods=['PUT'])
def update_password(id):
    try:
        account = db_session.query(Account).filter_by(id=id).first()
        if not account:
            return jsonify({'error': 'Account not found.'}), 404
        json_data = request.get_json()
        try:
            data = AccountPasswordSchema().load(json_data)
        except ValidationError as err:
            return jsonify({"error": err.messages}), 400
        old_password_hash = account.password_hash
        new_password = data.get('password')
        new_password_hash = generate_password_hash(new_password, method='sha256')
        
        similarity_ratio = SequenceMatcher(None, old_password_hash, new_password_hash).ratio()
        if similarity_ratio > 0.8: 
            return jsonify({'error': 'New password is too similar to the old password.'}), 400

        account.password = data.get('password')
        db_session.commit()
        return jsonify({'message': 'Password successfully updated.'}), 200
    except Exception as e:
        return jsonify({'error': str(e)}), 400

Kindly, advise how can I implement this task?

ussrback
  • 491
  • 2
  • 8
  • 22
  • 1
    You're testing how close 2 hash values are to each other? The principle of hashes typically is that even a tiny change will change the hash completely. – James Z May 20 '23 at 12:43

1 Answers1

-2

I have updated code to grab old hash, generate the new hash, then compared them and if they are 70% matching return error

def update_password(id, access_level, user_id):
    try:
        account = db_session.query(Account).filter_by(id=id).first()
        if not account:
            return jsonify({'error': 'Account not found.'}), 404
        if account.public_id == user_id or access_level==4:
            json_data = request.get_json()
            try:
                data = AccountPasswordSchema().load(json_data)
            except ValidationError as err:
                return jsonify({"error": err.messages}), 400
            old_password = data.get('old_password')
            new_password = data.get('new_password')
            
            if not check_password_hash(account.password_hash, old_password):
                return jsonify({'error': 'Invalid old password.'}), 400
            
            similarity_ratio = SequenceMatcher(None, old_password, new_password).ratio()
            if similarity_ratio > 0.7: 
                return jsonify({'error': 'New password is too similar to the old password.'}), 400

            account.password_hash = generate_password_hash(new_password, method='sha256')
            if access_level == 4:
                account.role_id = data.get('role_id')
            db_session.commit()
            return jsonify({'message': 'Password successfully updated.'}), 200
        else:
            return jsonify({"message":"you are not allowed to edit this account"})
    except Exception as e:
        return jsonify({'error': str(e)}), 400
ussrback
  • 491
  • 2
  • 8
  • 22
  • 1
    You don't grab the old and new has, but the old and new password as submitted by the user. That's a very important distinction, because the other way wouldn't work. – Joachim Sauer May 20 '23 at 16:28