[IPython-dev] pyzmq authentication

MinRK benjaminrk at gmail.com
Wed Jun 1 19:26:45 EDT 2011

On Wed, Jun 1, 2011 at 16:04, Jason Grout <jason-sage at creativetrax.com> wrote:
> On 6/1/11 1:33 PM, MinRK wrote:
>> On Wed, Jun 1, 2011 at 11:20, Fernando Perez<fperez.net at gmail.com>  wrote:
>>> On Wed, Jun 1, 2011 at 9:41 AM, MinRK<benjaminrk at gmail.com>  wrote:
>>>> What we have currently is extremely primitive, and only meant to
>>>> protect against accidental execution rather than
>>>> malicious intrusion. The key is sent and checked with every message.
>>> If I understand correctly the link Jason sent, and from a quick
>>> reading of the multiprocessing code, we should be able to use the same
>>> machinery to avoid sending/receving the keys.  The main functions that
>>> do the work in MP are in the 'connection' submodule, and they are
>>> really two standalone functions:
>>> def deliver_challenge(connection, authkey):
>>>    import hmac
>>>    assert isinstance(authkey, bytes)
>>>    message = os.urandom(MESSAGE_LENGTH)
>>>    connection.send_bytes(CHALLENGE + message)
>>>    digest = hmac.new(authkey, message).digest()
>>>    response = connection.recv_bytes(256)        # reject large message
>>>    if response == digest:
>>>        connection.send_bytes(WELCOME)
>>>    else:
>>>        connection.send_bytes(FAILURE)
>>>        raise AuthenticationError('digest received was wrong')
>>> def answer_challenge(connection, authkey):
>>>    import hmac
>>>    assert isinstance(authkey, bytes)
>>>    message = connection.recv_bytes(256)         # reject large message
>>>    assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
>>>    message = message[len(CHALLENGE):]
>>>    digest = hmac.new(authkey, message).digest()
>>>    connection.send_bytes(digest)
>>>    response = connection.recv_bytes(256)        # reject large message
>>>    if response != WELCOME:
>>>        raise AuthenticationError('digest sent was rejected')
>>> They work with objects that have a basic socket interface, but
>>> adapting this to zmq sockets should be possible.  Am I missing
>>> something?
>> We have 3 main channels:
>> Shell (XREQ-XREP)
>> Stdin (XREQ-XREQ) (I think?)
>> IOPub (PUB-SUB)
>> Of these, *only* the XREP can authenticate connections.  The reason is
>> that none of the other sockets actually know where messages came from,
>> so there's no mechanism for seeing if the sender previously had a
>> successful handshake because you actually have no idea who the sender
>> was.
>> Even the client's XREQ socket cannot authenticate replies, it must
>> trust that the replies are coming from the kernel.
>> That said, the only easy one is the one that is the most important -
>> the Kernel checking requests prior to execution. However, if you have
>> someone sniffing on the line, it's trivial to spoof an authenticated
>> socket - just copy the IDENT, which is sent in the clear over the
>> wire, and the kernel will have exactly no idea that you aren't the
>> original authenticated client.
> If we don't have the concept of connection, then maybe we can authenticate
> each request by "signing" the requests.  For example, we could send a hash
> of the concatenation of the shared secret and the content of the message.
>  So a message would look like:
> (signature, json_message)
> The receiving code would then do the same process: concatenate the bytes in
> json_message, right off the wire, with the shared secret, do the same hash,
> and check the signature.  This would provide some assurance that whoever
> sent the message knew the shared secret.

I don't see a reason why this wouldn't work.  If we don't have
one-time keys, then there's
nothing preventing sniffers from resubmitting the *same* message,
though it would protect
against arbitrary code.

The only disadvantage is that you are digesting potentially large
messages, but that's the way it goes.

> Another thing we could do for a sequence of messages is to always increment
> a sequence number appended to the shared secret before sending the message,
> and send the hash of the shared secret + sequence number. This provides a
> series of keys, but each is a one-time key (the receiver tracks the sequence
> number, so it can ignore people that try to reuse these hashes).

With multiple clients, there would need to be a counter *per client*,
since each client doesn't
know how many messages the other clients are sending.

> Jason

More information about the IPython-dev mailing list