The so called ‘cut and pasted code attack’ also known as ‘Frankenstein Monster Attack’ is an attack that the adversary swaps the ‘code’ in the authorization response with the victim’s ‘code’ that the adversary has gotten hold of somehow. It can be through the Code Phishing attack, or some other attacks. Then, he uses the ‘code’ in his browser session to feed to the client so that the client uses it to get the access token with victim user’s privilege. So, it is a kind of privilege escalation attack. The ‘code’ is a string that binds the front end session through the browsers and the back end session between the client and the authorization server. The adversary swaps this ‘binding string’ and swaps the session. So, it is a kind of session swap attack from a technical point of view.
You may ask “but the state variable is protecting the client from CSRF is it not?” Unfortunately, it is not adequate. Because the adversary uses the victim’s ‘code’ in his valid session, the CSRF protection through the ‘state’ variable does not work. This is because the ‘code’ is not bound to the ‘state’.
As usual, there are a few ways to mitigate this:
- Stop the ‘code’ leaking;
- Bind the ‘code’ to the ‘state’;
Since the basic assumption of this attack is that the ‘code’ has somehow leaked, if we can avoid it through for example the OAuth-meta, this attack is mitigated. See this post for more details.
The second one protects the client even when the ‘code’ was leaked. One way is to bind the ‘code’ and ‘state’ for example by baking the hash of the ‘state’ into the ‘code’ and have the client send the ‘state’ variable with its token request.
OpenID Connect’s hybrid flow falls into this category as well and mitigates the attack fine. OpenID Connect clients send a nonce in the request. At the same time, the nonce is stored in the session that is linked to the ‘state’. The nonce will be returned in a signed ID Token unchanged, so the ID Token and the ‘state’ is linked through session. The ID Token also includes ‘c_hash’, which is a hash of the ‘code’ returned. Thus, the ‘code’ and the session is also bound. If the adversary just swaps the ‘code’, the value of ‘c_hash’ will not match and the attack will be detected. Even if the adversary swaps both ‘code’ and ID Token, the attack will not work since the nonce in the ID Token and the session will not match.
So, we have three kinds of solutions to this attack. Which one should we take?
One important aspect of the security is that it has to be proportionate to what you are protecting. If the transaction is of low value, you do not spend so much on the mitigation.
OAuth use cases spans from very basic risk case to a higher risk scenario. In a basic risk case, you may want to take the risk of this attack provided the likelihood of the code leaking is low enough by implementing something like [oauth-meta].
On the other hand, in a higher risk scenario, you may want to implement more protection. Then, you can take either approach in the category 2. If the client and the server supports OpenID Connect, then simply use either the ‘code id_token’ flow or ‘code token id_token’ flow. If the client is implemented properly, it will solve the problem.
If the client and the server is not supporting OpenID Connect, then ‘Embed the state in the code’ approach may be better. But again, you have to ask yourself if the additional cost is worth. Option 1. might be good enough so think twice.
For the basic risk cases, IMHO, [oauth-meta] is good enough.
For a higher risk cases, just use OpenID Connect. But I could live with the ‘state’ baked in or associated with ‘code’ option as well. However, it should be an optional feature and should not be mandated.