Q:

AES: how to detect that a bad password has been entered?

A text s has been encrypted with:
s2 = iv + Crypto.Cipher.AES.new(Crypto.Hash.SHA256.new(pwd).digest(), 
                                    Crypto.Cipher.AES.MODE_CFB, 
                                    iv).encrypt(s.encode())

Then, later, a user inputs the password pwd2 and we decrypt it with:
iv, cipher = s2[:Crypto.Cipher.AES.block_size], s2[Crypto.Cipher.AES.block_size:]

s3 = Crypto.Cipher.AES.new(Crypto.Hash.SHA256.new(pwd2).digest(),
                           Crypto.Cipher.AES.MODE_CFB, 
                           iv).decrypt(cipher)

Problem: the last line works even if the entered password pw2 is wrong. Of course the decrypted text will be random chars, but no error is triggered.
Question: how to make Crypto.Cipher.AES.new(...).decrypt(cipher) fail if the password pw2 is incorrect? Or at least how to detect a wrong password?

Here is a linked question: Making AES decryption fail if invalid password
and here a discussion about the cryptographic part (less programming) of the question: AES, is this method to say “The password you entered is wrong” secure?
.

A:

AES provides confidentiality but not integrity out of the box - to get integrity too, you have a few options.  The easiest and arguably least prone to "shooting yourself in the foot" is to just use AES-GCM - see this Python example or this one.
You could also use an HMAC, but this generally requires managing two distinct keys and has a few more moving parts.  I would recommend the first option if it is available to you.
A side note, SHA-256 isn't a very good KDF to use when converting a user created password to an encryption key.  Popular password hashing algorithms are better at this - have a look at Argon2, bcrypt or PBKDF2.
Edit: The reason SHA-256 is a bad KDF is the same reason it makes a bad password hash function - it's just too fast.  A user created password of, say, 128 bits will usually contain far less entropy than a random sequence of 128 bits - people like to pick words, meaningful sequences etc.  Hashing this once with SHA-256 doesn't really alleviate this issue.  But hashing it with a construct like Argon2 that is designed to be slow makes a brute-force attack far less viable.

