[Twisted-Python] Re: cred and stateless protocols

Phil Mayers p.mayers at imperial.ac.uk
Wed May 10 04:42:32 MDT 2006


Manlio Perillo wrote:
>> The only sensible solution to HTTP authentication for important
>> applications is to use an HTTPS link, signed server certs and ideally
>> client certs as well, 
> 
> 
> Yes, but how many people knows how to set up a private certificate?
> And how many sites uses certs verification?

Lots. I honestly don't think it's that hard but we might have to agree 
to disagree on this. If it has a fault, it's the difficulty virtual 
hosting without burning IPs.

> 
> It would be nice to store a certificate on a smart card and authenticate
> to a web server using only that certificate.

You can do that now if you want. But the hardware is expensive. Frankly, 
I think a local software store with key is fine.

But the important thing is to use SSL and server certs. The client certs 
are just a nice-to-have.

>> an "Authorization: GoogleAuth THETOKEN" header. 
> 
> And this header should be supplied for every successive requests (like
> cookies)?

Yes, avoiding the need for an extra round-trip per request.

> 
>> This provides much
>> greater scalability and is similar to MS Passport (which itself is
>> similar to Kerberos).
>>
>> Presumably the token expires. You should note however that the token is
>> NOT used for sessioning. HTTP 302 redirects and URL parameters are used
>> for that. 
> 
> 
> I'm not sure to understand this.

GET /resource

401 unauth

GET /resouce
Authorization: GoogleAuth=foobarbaz

302 Moved
Location: /resource?sessionid=id

GET /resource?sessionid=id
Authorization: GoogleAuth=foobarbaz

200 OK
CONTENT

...then on subsequence requests, you can do:

GET /another_resource?sessionid=id
Authorization: GoogleAuth=foobazbaz

200 OK
CONENT

...which is a single round-trip with authentication and sessioning

> 
>> You might ponder that Google separated out auth and sessions
>> even in their engineering compromise.
>>
>> Note that the above refers to the non-browser API. Presumably the
>> browser API will use a passport-alike 302+cookie.
>>
>> For open source examples, see PubCookie.
>>
> 
> Thanks for the link.
> I have not read the source, but the "granting cookie" what type of
> informations contains to be sure that the UA is the "right" one?

I haven't used it, I just know it's generally well thought of.

> 
>>> By the way:
>>> for user tracking in UDP, why not just use the peer address?
>> Pardon? Are you serious?
>>
> 
> Well, let me explain this better.
> Unfortunately there are not examples of UDP servers in twisted.
> 
> Since UDP is connection-less, the first thing that come to my mind is:
> 
> class MyProtocol(DatagramProtocol):
>     def __init__(self):
>        self.users = {}
> 
>     def datagramReceived(self, data, (host, port)):
>         context = self.users.setdefault(host, Context())
>         response = context.handle(data)
>         self.transport.write(data, (host, port))
> 
> 
> Where the Context class keep an internal state, like IMAP.

That is phenomenally insecure

  1. You're using just the IP and not the IP+port, which means 2 users 
behind the same NAT will be unable to simultaneously use your service, 
or will see each others data.

  2. Since it's UDP it's trivially forged, so unless your context.handle 
FURTHER authenticates the data (via e.g. HMAC and key agreement) it's 
basically open to the world

  3. You're creating a new context for the 1st packet from each IP, so I 
can trivially send hundreds of thousands of packets to your service with 
forged source addresses and exhaust the CPU and memory resources of your 
server.

At ABSOLUTE MINIMUM a UDP protocol must force the client to round-trip 
the first packet using minimal CPU resources possible to at least ensure 
it's not a source-spoofing DDoS.

# WARNING WARNING WARNING DO NOT USE INSECURE IN MANY WAYS
# I strongly suggest the use of TCP or existing secure UDP
# protocols such as Q2Q
class proto(DatagramProtocol):
   MAGIC = 'MYPT'
   ECHO = open('/dev/random').read(16)

   # PDU format: MAGIC(4 bytes)+FLAGS(1 bytes)+PAYLOAD

   def datagramReceived(self, data, (host, port)):
     if len(data)<28:
       # too short
       return
     if data[:4]!=self.MAGIC:
       # not our protocol
       return
     flags = ord(data[4])

     # 1st packet in connection has flags=0
     if flags==0:
         # note: no state and minimal CPU consumed here
         self.transport.write(self.MAGIC+'\0'+self.ECHO, (host, port))
         return

     # 2nd packet must have flags==1 and payload==ECHO
     elif flags==1:
         if data[5:]!=self.ECHO:
             return
         # ok, we've verified there's something on the other
         # end, now start e.g. secure diffie-hellman
         context = startContext(host, port, self.transport)

     else flags==2:
         feedContext(host, port, data)

The above is NOT SECURE and would need sequence numbers, authentication 
and integrity protection adding. But it should hopefully convince you 
that the naive approach is just that - and in fact actively dangerous in 
a modern, hostile internet environment.




More information about the Twisted-Python mailing list