[IPython-dev] pyzmq authentication
Jason Grout
jason-sage at creativetrax.com
Wed Jun 1 19:04:55 EDT 2011
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.
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).
Jason
More information about the IPython-dev
mailing list