mohawk icon indicating copy to clipboard operation
mohawk copied to clipboard

`seen_nonce` warning in `Sender.accept_response`

Open jkeys089 opened this issue 6 years ago • 6 comments

I'm using v0.3.4 on python 3.6 and I'm seeing the following warning being logged during Sender.accept_response calls: seen_nonce was None; not checking nonce. You may be vulnerable to replay attacks

I'm confused since there is no nonce in the server's Server-Authorization response header. Is this possibly a bug or am I just misunderstanding?

jkeys089 avatar Dec 17 '18 23:12 jkeys089

Hi. Thanks for taking time to file an issue.

This message is warning you that that the request did not contain a nonce and that your receiver is not configured to check a nonce. This message is expected. By not handling nonces, an attacker may be able to intercept a request and replay it without altering the content. This might not do any damage, though. It depends on how your app processes repeat requests. More info: https://mohawk.readthedocs.io/en/latest/usage.html#using-a-nonce-to-prevent-replay-attacks

If I am misunderstanding, maybe you can provide me with the sender / receiver code (or at least the receiver part) as an example.

Feel free to re-open the issue!

kumar303 avatar Jan 09 '19 22:01 kumar303

Thanks for responding @kumar303.

Here is a reproducer that may help:

from mohawk import Receiver, Sender
from mohawk.util import parse_authorization_header

credentials = {'id': 'abc123', 'key': 'superSecr3t!', 'algorithm': 'sha256'}
url = 'https://example.com/test?id=1'
method = 'GET'

request_data = ''
request_content_type = ''

sender = Sender(credentials, url, method, request_data, request_content_type)
request_auth_header = sender.request_header

if not parse_authorization_header(request_auth_header).get('nonce'):
    raise Exception('missing nonce!')

receiver = Receiver(
    lambda id: credentials,
    request_auth_header,
    url,
    method,
    content=request_data,
    content_type=request_content_type,
    seen_nonce=lambda id, nonce, ts: False
)

response_content = '{"ok":true}'
response_content_type = 'application/json'
response_header = receiver.respond(content=response_content, content_type=response_content_type)

# this line prints: 'seen_nonce was None; not checking nonce. You may be vulnerable to replay attacks'
sender.accept_response(
    response_header,
    content=response_content,
    content_type=response_content_type,
)

As you can see, the client / sender is causing the warning, not the server / receiver.

jkeys089 avatar Jan 10 '19 18:01 jkeys089

Thanks for the code sample. I haven't tried to run this but I think it's because your Sender is not configured for nonce checking. When you run sender.accept_response(), the sender is verifying the signature of the server's response. It is a pretty wild edge case but it is theoretically possible that an attacker could replay server responses. If you were to add Sender(seen_nonce=lambda ...) then the warning would go away.

kumar303 avatar Jan 10 '19 19:01 kumar303

@kumar303 There is no nonce returned in the server's response (see https://github.com/hueniverse/hawk#response-payload-validation). Just to be sure, I also checked the reference client and it doesn't have any reference to a nonce (see https://github.com/hueniverse/hawk/blob/b117bce695bc6b531a5afda8dbbc46267759efdb/lib/client.js#L141).

Maybe I'm just misunderstanding but I don't think it makes sense to have a seen_nonce check on the client / sender.

jkeys089 avatar Jan 11 '19 18:01 jkeys089

Sample code which demonstrates my statement above:

from mohawk import Receiver, Sender
from mohawk.util import parse_authorization_header

credentials = {'id': 'abc123', 'key': 'superSecr3t!', 'algorithm': 'sha256'}
url = 'https://example.com/test?id=1'
method = 'GET'

request_data = ''
request_content_type = ''

def seen_nonce(id, nonce, ts):
    print(nonce)
    return False

sender = Sender(credentials, url, method, request_data, request_content_type, seen_nonce=seen_nonce)
request_auth_header = sender.request_header

if not parse_authorization_header(request_auth_header).get('nonce'):
    raise Exception('missing nonce!')

receiver = Receiver(
    lambda id: credentials,
    request_auth_header,
    url,
    method,
    content=request_data,
    content_type=request_content_type,
    seen_nonce=seen_nonce
)

response_content = '{"ok":true}'
response_content_type = 'application/json'
response_header = receiver.respond(content=response_content, content_type=response_content_type)

# this line raises `KeyError: 'nonce'` because there is no nonce in the response
sender.accept_response(
    response_header,
    content=response_content,
    content_type=response_content_type,
)

jkeys089 avatar Jan 11 '19 18:01 jkeys089

Oh, whoops! That definitely looks like a bug in mohawk. Thanks for all the detailed info.

kumar303 avatar Jan 11 '19 20:01 kumar303