1

I'm currently developing a web application using Node.js (server-side JavaScript) and MongoDB (NoSQL database).

I'm at the stage where I have to design the authentication and I had a question about asynchronous programming.

One of the advantages of using Node.js is that everything can be programmed to be asynchronous and thus non-blocking when doing I/O operations.

  • In the case of authentication, should I write everything synchronously?

I'm afraid that an attacker could spam my server with requests that would lead to the creation of duplicate accounts (or other objects) and maybe security issues later on.

MongoDB doesn't support transactions like SQL.

  • Am I right to worry about asynchronous authentication processes?
TildalWave
  • 10,801
  • 11
  • 45
  • 84
Ko Ichi
  • 27
  • 1
  • 2
  • 5
    [Some people, when confronted with a problem, think, "I know, I'll use threads," and then two they hav erpoblesms.](https://twitter.com/nedbat/statuses/194452404794691584) Seriously, multithreading opens up a *world* of possibilities for subtle, hard-to-reproduce security bugs. You are right to worry about it. – Ladadadada Jun 19 '13 at 09:03

2 Answers2

1

Handling the requests synchronously on an event based server leaves you open to easy DOS, and certainly has a very negative impact on performance. Regardless of what type of server you use, a robust authentication system should provide some protection against brute force scanning.

The most common practice I've seen for protecting against brute force scanning is to disable an account after a certain number of failed logins - which, IMHO, is a DOS itself. A more practical approach is that implemented by fail2ban - after a certain number of failed attempts from a particular remote address, that address gets a temporary ban at the firewall.

The solution I've used in the past is to implement short queues for mutexes on the username and source IP address - the authentication is not tested until a request has both mutexes. If either queue is full then the request is failed immediately. This still provides some scope for DOS, but its scope should be far better contained.

All of these can be handled asynchronously.

symcbean
  • 18,278
  • 39
  • 73
  • Thank you for your quick answer! About timeouts, are logarithmically increasing timeout periods effective enough or should I setup a "X failed logins == account ban" policy ? – Ko Ichi Jun 19 '13 at 10:01
  • @KoIchi - think this should be asked as a separate question, in its own right. – Deer Hunter Jun 19 '13 at 10:20
1

When dealing with synchronism, one good way of thinking is about state. State is whatever must be retained in some data storage (which can be RAM, disk,... whatever, and not necessarily on the server) so that processing may ultimately proceed and succeed.

When doing synchronous calls (generically, not specifically with Node.js), the caller is an execution thread on some machine, and the "state" is what that execution thread "knows": its current position in the code, its local variables, its call stack. The caller retains that state in RAM for the duration of the call; that's the definition of a synchronous call: while the call is not finished, the caller is blocked. Details may vary depending on the programming language and how it is interpreted/compiled, but for most languages, each execution thread (a language concept) is mapped on a system thread (an operating system concept), which means that the state includes an entry in the system table of threads, a bit of space in the scheduler structures, and the thread stack.

Each system thread has its own stack, allocated when the thread was created. Default size for this stack depends on the OS, and might be programmatically adjusted. On Linux, a thread stack has size 1 MB by default. That's 1 MB of address space, not of RAM: actual pages are allocated when they are accessed, so a typical thread will use only a few dozen kilobytes of "true RAM". Though details vary a lot depending on context, the bottom-line is the following: in a typical application, you can normally handle a few hundred threads at any one time -- which means that your server can host a few hundred ongoing synchronous calls simultaneously. Beyond that, you must think.

Asynchronous calls remove this thread-based state management: the caller regains control immediately and can thus do something else or even exit. However, you must still keep some state somewhere, if only to remember that there is an ongoing call and to know what should be done with the result. The point of going asynchronous is to make manual, fine-grained management of this state, which can be as small as a 16-byte session ID or some similar core state, thus allowing for a lot more simultaneous calls. Depending on context, you might even be able to offload state on the client (which implies extra security issues to deal with, of course). However, implementation becomes a great deal more complex when going asynchronous, and complexity usually comes with extra work, extra bugs, and thus extra vulnerabilities.

So going asynchronous is an optimization, and thus should be envisioned only if the situation makes it worth the extra complexity and effort. As with all performance things, such optimizations should be delayed until an actual serious performance-related problem has been duly noticed, monitored and measured. "Premature optimization is the root of all evil."

Therefore I suggest that your first write all your code synchronously. Then, and only then, will realistic performance tests tell you whether synchronous calls will be enough or not; and if going asynchronous is indeed warranted, at that point you will know a lot more about your own application, making you ready (or at least readier) to tackle the inherent complexity of asynchronous programming.

Tom Leek
  • 168,808
  • 28
  • 337
  • 475