Is it necessary to sign or will a modified encrypted string fail to
decrypt?
In the cryptography world a modified message is called a tampered message. Encryption by itself does not provide protection against tampering, but block ciphers are not used alone. Block ciphers are used in different modes, f.e. ECB, CBC, CTR, GCM, etc. Different modes have different properties, some of them provide protection against tampering (a.k.a. Authenticated Encryption, f.e. GCM) and others not.
If you're using an Authenticated Encryption mode a tampered message will fail to decrypt. But, if you're using others modes like CBC or CTR (These are the most widespread in my experience) a tampered message will happily decrypt unless there is a padding error
If it doesn't result in valid json which can be parsed into a session
it will be empty in the same way that it would with an invalid
signature.
This will depend on how your application handles an invalid json, and here is where a padding oracle may appear.
My understanding is that padding oracle attacks work by getting
feedback from the server. Would an encrypted empty json string be
enough?
A padding oracle attack occurs when the attacker sends a tampered message and is able to distinguish if the padding is correct or is not. And this is why I said previously that how your application handles an invalid JSON may derive into a padding oracle
Imagine that an attacker sends a tampered message with a wrong padding, your application will try to decrypt the message but a wrong padding exception will throw. After that the attacker tampers the message again but this time he's able to forge a correct padding, your application will decrypt correctly the message but the content will be and invalid JSON, the application will attempt to parse the invalid JSON and throw an error as it can't be parsed
If the attacker is able to distinguish both scenarios cause the HTTP status returned is different, or a stacktrace shows the different error messages, or just by timing the response time. Then the attacker has a padding oracle and can recover the plaintext from the cookie
To prevent this you should always provide same error message when the message can't be decrypted and when the message decrypts but the content is invalid. Furthermore you should ensure that both scenarios require the same time to respond.
IMO the best approach would be to use an authenticated encryption mode that gives you encryption and signing together and let the encryption framework/library handle it
BTW, I mentioned an encryption mode that should never be used. It's ECB, if you're using this mode (Some libraries default to it) please change it as it's a weak mode