January 2nd, 2004

SSL, challenge/response -- questions?

I just announced in news our new SSL and challenge/response code:


But it was necessarily dumbed down a bit. Some more details:

-- challenge/response was done both to limit SSL load and also so we can do it everywhere eventually (including on comment post pages from journals)

-- the protocol handlers will support challenge/response and Digest auth soon

-- avva is finishing up Digest auth, and we'll probably support it globally on any URL, by adding ?auth=digest, which will override $remote (LJ::get_remote())

Um, I'm forgetting a lot. Any questions?

Update: I remembered what I was going to post after scsi from DeadJournal.com reminded me. How to setup SSL!

$LJ::USE_SSL = 1; # you want the site to promote/use SSL
$LJ::SSLROOT = "https://www.foojournal.com"; # url prefix for secure area
$LJ::SSLDOCS = "/home/foojournal/ssldocs"; # system path to "ssldocs" (like htdocs)

Now, you either do SSL from say mod_perl:

SSLEngine on
SSLCertificateFile /etc/apache/server.crt
SSLCertificateKeyFile /etc/apache/server.key
SSLVerifyClient 0
SSLVerifyDepth 10

ServerName www.lj.com
PerlSetEnv LJHOME /home/lj
PerlRequire /home/lj/cgi-bin/modperl-ssl.pl

Or, what we do, is you have an SSL proxy out in front (BIG-IP, mod_ssl+mod_proxy, Pound, whatever) that does SSL to clients, but speaks plain HTTP to the backend.

But then how does the backend know it's supposed to do the HTTP-thing?

Define a hook, like we do in cgi-bin/ljcom.pl:

LJ::register_hook("ssl_check", sub {
my $r = $_[0]{r};
$r->header_in("X-LJ-SSL") ||
($LJ::IS_DEV_SERVER && $r->header_in("Host") eq "secure.$LJ::DOMAIN");

It gets a hashref with key 'r' (for the mod_perl $r) and returns a boolean. We look for the X-LJ-SSL header, or a "secure.foo.com" domain name. (for testing)

Now, in cgi-bin/Apache/LiveJournal.pm, the code look to see if that hook returns true, and sets: $LJ::IS_SSL to 1 or 0. So the rest of the code on the site can look at $LJ::IS_SSL.

Er, actually, if you don't do an SSL proxy, you'll need to make your hook above look at something besides the header, like check to see if mod_perl is using SSL directly (but I forget the code for that). Somebody get me a patch and I'll make that part of the core, not requiring a hook.

Anyway, in addition to $LJ::IS_SSL, LiveJournal.pm also fixes up $LJ::IMGPREFIX and $LJ::STATPREFIX to be relative URLs, so all images/javascript also come from SSL and don't invoke browser warnings.

Have fun!

Identity servers

One thing I've been interested in for a while and wanted to get working on in 2004 is distributed identity servers.

I want each LiveJournal installation to be an identity server, and other sites can selectively get your identity info, if you give them permission to have it (and for how long).

Along with namespace separation, this will let LiveJournal and DeadJournal users roam between the sites, leaving comments on each other's stuff. And hell, we might even support each other's userinfo head icons, so when a DeadJournal person comments on your post, you see a skull icon. But that's quite a ways off.

hozed is interested in all this too, and brought up a more global effort at this:

Project Liberty

We might just want to support that instead. (I haven't finished reading about it, but it looks thorough.)

internationalization qualms

timwi, you asked once why we don't do translation strings from the beginning of new features, and we replied vaguely that it makes updates hard as the UI takes shape. You were correct that we don't always get to translation strings too quickly (if ever) afterwards, but didn't understand why things were a pain during the "UI is very liquid" stage. Here's the reason why:

The thing I hate most about the translation system is that there's no way to update a translation strictly in a patch, short of deleting the string and making a new key.

The reason is: when you run bin/upgrading/texttool load, it doesn't update the live database with what you have in your lang.dat files if any version already exists. Why? Because it thinks the version on the site might be newer, and won't risk destroying it. That paranoia was designed it, and it's saved our asses tons of times. But it makes real updates pain:

When I go to make a simple patch which fixes a relative URL (like from ssldocs/login.bml) to a full URL, so it works in both htdocs and ssldocs, I have to go through a dozen hoops:

Destructive way:
-- kill the translation string, remove from en.dat, and put a new entry in deadphrases.dat
-- make a new translation string.
(but I might've reverted changes in the translation string from the live site)

Annoying way:
-- remember to make the change at http://www.livejournal.com/interface/ when I put the new code live.
(but I always forget, and other people following the code don't want to do that).

So some thoughts:

Potential Solution #1
We already keep track of modtimes for translation strings in the database from the /translate/ interface. So let's make the dumptext/loadtext respect a new parameter:

Instead of:
/create.bml.entry.text = Make a new account!

Could use a time:
/create.bml.entry.text@1073105753 = I changed this recently.

(Where that number is the unix time since the epoch, no timezone. Run "date +%s" to get it.)

Now, dumptext will include that, and loadtext will replace the value on the live site if the time in the dat file is newer.

We'd just have to change the DATETIME column in the database to be an INT UNSIGNED. (currently it has timezone data implictly attached to it, which we don't want). Or, we could just say the DATETIME is GMT, and make mysql date formats in Perl (we do this elsewhere in the code) instead of using MySQL's NOW().

Potential Solution #2
The server also keeps a history of old values for each string. The .dat file to live site replacement policy could be: "If this string has never been used, it's newer. If, however, it was previously used for this key and it's no longer the newest, don't replace it, because that'd essentially be reverting it."

Personally, I prefer #1 because it's more explicit and faster. (I think #2 would take forever when running loadtext)

Thoughts? I'm going to go ahead with #1 sometime this week probably unless I hear either a) alternate solutions, or b) volunteers who want to implement #1/alternate solutions.

challenge/response for protocol handlers

I just committed a new protocol method (for both XML-RPC and "Flat" flavors) called "getchallenge" to get a challenge token to be later used in other protocol modes instead of sending the clear-text "password" or the just-as-bad (replayable) "hpassword" (md5'ed password).

Here are the flat results of the getchallenge method: (it takes no parameters or auth)


Ignore "auth_scheme = c0" for now. It might be used in the future if we implement other auth schemes or change or auth scheme. In that case we'd add a new capabilities exchange: client would say, "I know c0 and c1". Server would then say, "Use c1, it's the best." But it's just c0 for now, and always will be as long as you don't specify what you know. (if you want to be paranoid, you can make your client check for "c0")

(expire_time - server_time) is how long you have to use that challenge. (currently always 60 seconds) You'll currently get an "invalid password" error if you challenge expires, but we might change that to "challenge stale" in the future. In any case, get the challenge right before you do any other action. Getting a challenge is incredibly fast.

The real meat is "challenge". Don't try to dissect or understand it. It's an opaque cookie. Read the LJ source if you care. (cgi-bin/ljlib.pl, sub get_challenge)

Now, to use it send "user" (flat) or "username" (xml-rpc), then:

key "auth_method" with value "challenge"
key "auth_challenge" with the challenge you got, and:
key "auth_response" of MD5_hex(Challenge + MD5_hex(Password))

Don't send "password" or "hpassword". (that's the whole point).

The challenge can only be used once. You'll get "invalid password" errors if it's used more than once.

This code is live on the test server: http://test.livejournal.org/ It'll be live on the real site in the next day or two, if not sooner.

Enjoy! Any questions?

Collapse )