lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


It was thus said that the Great HyperHacker once stated:
> 
> Option 3 seems like the answer, but there's no way to read *up to* a
> certain number of bytes. If I specify 32, and the client only sends
> 16, the read will block forever or time out (and will *not* return
> those 16 bytes). So, the server would have to read one byte at a time,
> or else fail to accept legitimate requests. Of course this is terrible
> for performance.
> 
> To properly defend against this attack, the server needs to be able to either:
> 1) Peek into the receive buffer, and don't actually read the data
> (removing it from the buffer) until a line break appears, rejecting
> the connection if too much data is buffered without a line break, or:
> 2) Read all data received in the next x seconds or the next n bytes,
> whichever comes first. (Bonus if "or until the next line break" is
> also an option.) The server can then buffer lines in the same manner
> as option 1.
> 
> Unfortunately, LuaSocket doesn't seem to offer methods to do either of
> these, thus leaving every server using it vulnerable to such attacks.

  Unfortunately, the low level socket interface doesn't offer said methods
either (at least under Unix; I have no idea how Windows handle sockets at
the lower levels).  The three low level (read: this is what is ultimately
called) functions to receive data from a socket are:

	read	(int fd,void *buf,size_t count);
	recv	(int fd,void *buf,size_t count,int flags);
	recvfrom(int fd,void *buf,size_t count,int flags,struct sockaddr *addr,socklen_t *addrlen);

  The first parameter is the socket connection, the next two describe the
destination to read the data into and how much; flags modify what type of
data to read and usually this paramater is 0 but could be, at least under
Linux:

	MSG_PEEK	read incoming data, but the data returns is still
			treated as unread
	MSG_OOB		retreive out of band data.  Protocol specific.
	MSG_WAITALL	wait for all the data specified to be returned

  The last two parameters for recvfrom() fill in the remote address.

  Your possible answers still have issues:

1) peeking into the buffer is still subject to denial of service attack.  As
an attacker, all I need to do is:

	while(1)
	{
	  write(yourwebserver,"x",1);
	  sleep(1);
	}

And not only will you never see a line return, but I've tied up one
connection for a *very* long time until you consider I've sent a "very long
line".  Also, using the MSG_PEEK for this means memory is wasted in kernel
space buffering and thus may be a bad thing, depending upon the operating
system in question.

2) is probably your best bet, but you need to provide the proper buffering,
because at the lowest level, there is no "line buffering" of socket
connections.

  LuaSocket appears to be a wrapper around the recv() and recvfrom() calls
(at least for Unix).  These functions, as you may have noticed, do not
timeout on their own.  For timeouts, you have several options:

	1) use the alarm() function to interrupt the recv()/recvfrom() call. 
	I do not recommend this route as it deals with signal handlers,
	which are horrendous to get right.

	2) select() or poll() (or more esoteric variations on these,
	depending upon the operating system).  The more esoteric variations
	tend to have better performance, but aren't portable from system to
	system (Linux has epoll(), Solaris uses /dev/poll, *BSD has
	kqueue()), but failing that, you want to use poll() and only if you
	have to, select().

  Yes, doing the line buffering is a pain.  I haven't used LuaSockets enough
to say if it does the buffering for you, but a quick search of the code does
show buffer_meth_receive() checking for a "*l", which leads me to believe it
may do such buffering, but you'll have to check the documentation (or the
code itself) to make sure.

  I can outline the code you'll need, but you'll need to fill in some
details.  It will look something like (system calls I'd use in C are
designated as system.something()):

	-- connection is established
	-- connection is sock
	lastread = system.gettimeofday() -- returns current time to microsecond 
	readset = { sock }

	line = ""
	numreads = 0

	while true do
	  rc = system.select(readset,nil,nil,TIMEOUT)
	  if rc == 0 then
	    handle_timeout()
	  elseif rc < 0 then
	    handle_error()
	  end

	  -- socket is read to read
	  data = system.recv(sock,0) -- read data from socket, no flags
	  if data:find("\n") then
	    -- return line .. data, minus the portion past the '\n'
	    -- which needs to be saved for the next line
	    -- also, you can check here to see if the line is too long,
	    -- or contains malicious data or what have you
	  end
	  line = line .. data
	  numreads = numreads + 1
	  if numreads > THRESHHOLD then
	    -- we've made too many calls to recv(), handle
	  end
	  now = system.gettimeofday()
	  if (now - lastread) > TOOMUCHTIME then
	    -- or you could go by how long it's taken to reach this point
	    -- in time.  
	  end
	  lastread = now
	end

  Code is untested pseudocode, but it's approximately what you need to do. 
Lord knows I've written enough code (in assembly and C) like this in the
past.

  -spc (All abstractions leak, by the way ... )