Saturday, November 1, 2014

A standardized mistake is still a mistake

The software industry, like most technical industries, is plagued with the necessary evil of standardization.

Standards certainly are necessary. Without them, the software industry would not be making much progress.

But they can be very evil, too.

Consider, for example, the ancient (in software terms) LDAP standard, most recently re-issued about ten years ago as RFC 4510 and its sub-standards (4511 through 4519).

These standards cover a piece of software called a "directory server".

  • A directory is a fairly simple database, and is most commonly used to hold a database of usernames and other information about those users, such as their passwords.
  • And a directory server is most commonly used to provide an organization-wide security feature in which you can use the same password to log in to multiple pieces of software, as long as all of those pieces of software authenticate against the same directory server.

So, it's a fairly nice thing to have, and so it's become standarized and common.

But evil hides within this standardized mechanism. Lots of evil.

Consider one particular bit of evil, as discussed in Section 5.1 of RFC 4513, Simple Authentication Method.

This section describes how you can use a directory server to authenticate a user:

Servers that map the DN sent in the Bind request to a directory entry with an associated set of one or more passwords used with this mechanism will compare the presented password to that set of passwords. The presented password is considered valid if it matches any member of this set.

That's pretty good. In fact, that's the meat of the whole feature, right there: you can use this "directory server" thing to authenticate your users, by making a Bind request with the user name and the password that they gave you, and find out if their password is valid.

That's a really important feature. You shouldn't build your own password management and authentication functionality, after all; you should use an established, standardized mechanism.

So, cool.

But, uh-oh, look at what the standard contains, right here, in Section 5.1.2, Unauthenticated Authentication Mechanism of Simple Bind:

An LDAP client may use the unauthenticated authentication mechanism of the simple Bind method to establish an anonymous authorization state by sending a Bind request with a name value (a distinguished name in LDAP string form [RFC4514] of non-zero length) and specifying the simple authentication choice containing a password value of zero length.

What's that?


What this says, in simpler words, is:

If you try to use a directory server to authenticate your users, it is YOUR JOB to make sure that the password that the user types in is at least 1 letter long, because the directory server doesn't check that!

That is, in even simpler words:

WARNING! There is a HORRIBLE bug here, but it's been here since the beginning and so we standardized the bug.

I mean, really, it should have been obvious, when the committee sat down to write this manual, when somebody typed in the words:

Unauthenticated Authentication Mechanism

That should have been a full blocker right there. I mean, can you imagine? Just say those words out loud to yourself:

Unauthenticated Authentication Mechanism


Now, the authors of RFCS 4513 do at least try to make this clear (on page 14 of a 33-page document that is 1 of 10 documents that define how directory servers work):

The distinguished name value provided by the client is intended to be used for trace (e.g., logging) purposes only. The value is not to be authenticated or otherwise validated (including verification that the DN refers to an existing directory object). The value is not to be used (directly or indirectly) for authorization purposes.

Unauthenticated Bind operations can have significant security issues (see Section 6.3.1). In particular, users intending to perform Name/Password Authentication may inadvertently provide an empty password and thus cause poorly implemented clients to request Unauthenticated access. Clients SHOULD be implemented to require user selection of the Unauthenticated Authentication Mechanism by means other than user input of an empty password. Clients SHOULD disallow an empty password input to a Name/Password Authentication user interface.

This is the language of a standards committee waving their hands.

This is the language of a standards committee saying:

If you have the bad luck to try to write a program which uses a directory server to authenticate users, and you haven't had the good fortune to stumble across these few paragraphs on page 14 of document 4 of an 10-volume manual set written many years ago, you're almost certainly going to build a gigantic horrible super-critical security bug into your program.

Sorry about that.

And this isn't just a theoretical issue; these bugs exist, and they're super-critical, and they're being introduced all the time! Here's one: ldap authentication with blank password

When using LDAP authentication, providing a blank password is treated as a successful login. The cause is that the back-end LDAP server treats a bind with an empty/blank password as an anonymous bind, and I suspect the LDAP code in artifactory simply looks for a successful bind. This probably only happens with certain LDAP server implementations, but SunONE LDAP (which we use at my site) does this.

These bugs, unfortunately, are all over the place.

Here's one.

Here's one.

Here's one.

Here's one.

Believe me: there are tens of thousands of these bugs; they might be getting created faster than they are being found and fixed. There are so many of these bugs, that people write entire research papers just on the topic of how this introduces so many bugs..

The people who provide directory servers, of course, aren't terribly proud of this, and many modern directory servers do at least default to having this feature turned off, which helps a little bit. But they generally don't make a lot of noise about MASSIVE SECURITY HOLES IN THEIR OWN SOFTWARE WHICH THEY WERE REQUIRED TO PROVIDE BECAUSE IT'S PART OF THE STANDARD. Instead, they say things like:

the default behavior is for server to return a unwillingToPerform result code when someone tries to bind using a null password.

We won't go any deeper into this 'feature', those interested in the rational behind it and the associated drawbacks can read the following links :

This is documentation written by a programmer who's trying to tell you:

I didn't want to provide this feature.

Please don't ever turn this feature on.

If you ever think you want to turn this feature on, go read this 33-page manual that says why I had to let you turn this feature.

It's just a mess, and has led to the world being sprinkled with well-meaning but confusing web pages hinting at the evil that lurks within:

WARNING: An attempt to bind with a blank password always succeeds because the LDAP protocol considers this to be an "anonymous" bind, even though a username is specified. Always check for a blank password before binding.

It's hard to write software, and it's hard to write standards, and it's certainly hard to write software standards.

But if you ever find yourself on a standards committee, working on a standard, try not to be That Guy.

Try not to standardize a mistake.

Just pretend you're Gandalf, standing there as the evil approaches, and scream at the top of your lungs:

You Shall Not Pass!

No comments:

Post a Comment