One often important, and often overlooked aspect of modern web development is Open Graph tags. You know, those meta tags with weird attributes that break your page validation? That’s a whole other topic though.
Today, I want to talk about the Facebook Object Debugger, and giving it access to an HTTP Auth protected environment, such as a staging or pre-launch production site. This is Apache specific, so nginx fans will have to look elsewhere.
Assume you have this setup in your Apache config or htaccess;
AuthName "Secure Area"
The easiest way that I’ve found to make this work is to accept based on user agent. I originally tried allowing it based on IP address, but the debugger uses many IP addresses, and after I had added a half dozen I gave up and switched to user agent.
Be aware, that because of this, it’s quite easy for someone to fake their UA and gain access, so I recommend only using this code while you actively use the debugger, and turning it off afterwards. This also prevents leaks if someone pastes the URL into an actual Facebook comment.
AuthName "Secure Area"
# Allow from Facebook
SetEnvIfNoCase User-Agent facebookexternalhit.* facebook
Allow from env=facebook
Check out this page at AskApache for a nice guide to SetEnvIfNoCase.
I’m going to get on a programming soapbox real quick and cover a topic that seems to confuse some people.
Hashes Are Not *$&%@! Magic
Some people seem to think that swapping out a secret with a hashed version of that secret makes it all safe and cozy, but that’s simply not true.
Yes, cryptographic hashes are a very important part of digital security, for a number of good reasons, but they have to be applied in a manner which takes the whole system into account.
The impetus for this work was a login integration I recently updated, because some other developer foolishly applied hashes.
Essentially, we were cross-posting a login form on one website to another. Nothing fancy. Ignore the lack of CSRF control.
<form method="POST" action="http://theotherguys.saas/login">
<input type="text" name="user" />
<input type="text" name="password" />
<button type="submit">Log In</button>
The New Form
But the new form would need a change. Instead of sending the username and password, we would send the username, and an MD5 hash of the concatenation of username and password.
Now, I’m sure when this idea was implemented, it was sold as a way to authenticate the user, without exposing their password in plaintext (note that they don’t use SSL). Brilliant!
Yes, it does obscure the plaintext password, but it is not any more secure.
You see, they didn’t think about the system as a whole, they were just focused on obscuring the password.
All that happened here is a substitution of shared secrets.
Previously the server compared the username and password it has on file to what was sent in. Now it compares the username and the hashed password to what it has on file. Do you see what we did? We’ve simply swapped the secret of the plaintext password for the secret of the hashed password. I can still intercept your form submission over the wire and steal your credentials.
I don’t have to prove I know the password, I have to prove I know the secret.
Zero gain, and you’ve added complexity.
But MD5 is weak. And we have the salt, if you can call it that, in the username. An old 2Ghz P4 can try about 20 Million hashes a second, and throwing a modern GPU at it you can test several billion hashes a second. If we want the plaintext password, we can get it unless it is reasonably large (7+ characters) and fairly complex (at least one non-alphanumeric character).
For an extra thought, consider how they must be storing these passwords. Either there scheme has always been MD5(CONCAT(username,password)) or they are storing them in plaintext and are (hopefully) migrating to hashed.
“Winners ship. Period.”
- Kendall Clark
“We used to design ways to get to the moon; now we design ways to never have to get out of bed.”
- Mike Monteiro
Design Is a Job