/mod-auth-ticket-for-lighttpd

Auth module which uses signed cookie for authentication. Developed to protect whole server using app-level SSO (OpenID, OAuth, etc).

Primary LanguageC

mod_auth_ticket for lighttpd

=== What is it? ===

This is a lighttpd module that

  1) controls access to web page using signed cookie authentication, and
  2) maps verified cookie content into HTTP Basic Authentication header.

Initial goal of this module was to map OpenID/SSO mechanism
onto HTTP BasicAuth mechanism, so I can move various legacy
HTTP-BA based services under the hood of Google Accounts SSO.

This module was initially called "mod_auth_cookie", but it
turned out to be a bad name as the original (mod_auth_cookie for Apache)
never provided any kind of security - which this module do provide.

=== How does it work? ===

This module does not directly verify user identity, but checks
if UA has supplied verifiable authorization data in a cookie.
Once verified, this module accepts whatever was in that
encrypted cookie as user identity.

Since embedding plain username/password in a cookie is a
big NO-NO, this data is encrypted and signed by its issuer,
in many case, external OpenID RP logon page.

Typical session works as follows:

  1. User tries to access protected page ("Page#1").

  2. mod_auth_ticket checks for a cookie, and finds no
     verifiable cookie exists. So it asks for authentication
     by redirecting UA to external logon page ("Page#2").

  3. From Page#2, user is guided to SSO (OpenID, etc) logon,
     and comes back with verified identity.

  4. At Page#2, verified identity is encrypted and signed
     with shared key between Page#1 and #2, and finally
     redirected back to Page#1.

  5. mod_auth_ticket examines cookie. Once verified, random
     token is issued as an access token (cookie), which replaces
     this incoming cookie. Hereafter, UA will only carry that
     random token - identity data will only be managed internally
     by mod_auth_ticket.

  6. mod_auth_ticket maps identity info to HTTP-BA header, so
     webapps can recognize user identity by $REMOTE_USER.

  7. Finally, the user is now allowed to view Page#1.

This is a lengthly process, but this trampoline-like flow is
needed to map complex OpenID-like authentication onto HTTP-BA
mechanism.

I first planned to implement mod_auth_openid which provides
OpenID RP feature directly, but abandoned as it'd be much
flexible to handle it in web application code. This modules
provides minimal feature needed by external OpenID RP to
inject identity information to HTTP-BA layer.

=== Configuration ===

  $HTTP["url"] =~ "^/secret/" {
      auth-ticket.loglevel = 255

      # Ignore incoming Authorization: header
      auth-ticket.override = 2

      # Life-duration of generated auth token
      auth-ticket.timeout  = 3600

      # Cookie name and option to use
      auth-ticket.name     = "TestAuth"
      auth-ticket.options  = "path=/;"

      # URL of logon page to redirect to in case of unverified cookie
      # Redirected URL will have a form of "/login.php?url=original-url"
      auth-ticket.authurl  = "/login.php"

      # Shared key used to encrypt and sign cookie payload
      auth-ticket.key      = "shared-secret"
  }

=== How to encrypt and sign cookie ===

Following is a sample code for generating verifiable auth
cookie using PHP.

  <?php
  // check identity
  if (! check_user($_POST["username"], $POST["password"])) {
      header("Location: login.php");
      exit(0);
  }

  // core encryption functionality (basically an XOR)
  function encrypt($buf, $key, $keylen) {
      $n = strlen($buf);
      for ($i = 0; $i < $n; $i++) {
          $c = ord($buf[$i]);
          $c ^= ($i > 0 ? ord($buf[$i - 1]) : 0) ^ ord($key[$i % $keylen]);
          $buf[$i] = chr($c);
      }
      return $buf;
  }

  # create time-based temporal key for encryption/sign
  $key = "shared-secret";
  $now = time();
  $now = $now - $now % 5;
  $tmp = md5($now . $key, TRUE);

  # encrypt and sign
  $plaintext = base64_encode($_POST["username"] . ":dummytext");
  $encrypted = bin2hex(encrypt($plaintext, $tmp, strlen($tmp)));
  $signature = md5($key . $now . $encrypted);
  $totaldata = "crypt:" . $signature . ":" . $encrypted;

  # set as cookie, so mod_auth_ticket willl see it in further use
  setcookie("TestAuth", $totaldata, 0, "/", "", FALSE, TRUE);

  # redirect back to original protected page
  header("Location: /protected/page.php");
  ?>

=== TODO/WISHLIST ===
- Clean up string/buffer handling
- Should clean up expired entries to free memory
- Introducing "srp:" cookie (encryption with Secure Remote Password)
- Allow authinfo injection using URL (for distributed auth)
- Add demo in other programming languages
- Make it compatible with Apache mod_auth_tkt/mod_auth_pubtkt.