cancel
Showing results for 
Search instead for 
Did you mean: 

Converting Digest to Basic Authentication

There are two common methods of Authentication built into the HTTP Protocol: Basic and Digest. They both work by using the "WWW-Authenticate" server response header, and the "Authorization" client request header. The simpler and more common of the two is the Basic method. This simply encodes the username and password with Base64 encoding and submits it to the server in the Authorization header. By contrast the Digest method is far more secure and works without sending the username and password over the network. Instead the client is sent a nonce challenge by the server, which it must then hash with the password and other data to generate an authentication response. The hash is put in the Authorization header along with other data and sent back to the server. If the server calculates the same response value as the client did (by using information in the header, and the username and password), then the authentication is successful.

 

Fortunately (??) Digest Authentication is open to a Man-In-The-Middle attack if you control a proxy through which the client and server communicate. So should you find yourself in a situation where you need to convert authentication schemes, because your server will only accept Digest authentication, but  your client only knows how to do Basic, then Stingray can help!

 

The below rule will convert a Server request for Digest authentication into a request for Basic authentication. It will then convert a Basic authentication request from the client into a Digest authentication request to the server. The rule has been tested to work with Apaches auth_digest module, which uses a QOP of auth. Your mileage may vary with other Digest implementations.

 

sub parseResponse() {

   # Return if the response is not a 401
   $code = http.getResponseCode();
   if ( $code != 401 )
      return;

   # Return if the auth is not Digest
   $authHeader = http.getResponseHeader("WWW-Authenticate");
   if ( ! string.contains($authHeader, "Digest") )
      return;

   # Parse the header...

   $auth = parseDigestHeader($authHeader);

   # We only support "auth", if it's not an option, log a warning...

   if ( ! string.contains($auth["qop"], "auth") ) 
      log.warn("Rule only supports \"auth\". Server QOP options: " .  $auth["qop"] );
      return;

   }
 

   http.setResponseCookie("digest", string.base64encode($authHeader));
   http.setResponseHeader("WWW-Authenticate", "Basic realm=" . $auth["realm"] );


}


sub parseRequest() {
   

   # We only support Basic Auth conversion, return if this isn't one
   $authHeader = http.getHeader("Authorization");
   if ( ! string.contains($authHeader, "Basic") ) 
      return;

   # Decode the user and password
   $enc = string.skip($authHeader, 6);
   $user = string.split(string.base64decode( $enc ), ":"); 
   
   # Get digest information from cookie or return
   $digest = string.base64decode(http.getCookie("digest"));
   if ( ! $digest )
      return;

   # Parse the decoded cookie digest
   $auth = parseDigestHeader($digest);


   $digest = generateDigest($user, $auth);
   http.setHeader("Authorization", $digest );

}


sub parseDigestHeader($header) {  
   foreach ( $param in string.split( string.skip($header, 7), ",") ) {
      $param = string.trim($param);
      if ( string.regexmatch($param, "([^=]*)=\"(.*?)\"") ) {
         $auth[ $1 ] = $2;
      }
   }

   return $auth;

}


sub generateDigest($user, $auth) {   
   $uri = http.getPath();
   $method = http.getMethod();
   $nc = 1;
   $now = sys.time();


   $ha1 = string.lowercase(string.hexencode(string.hashMD5( $user[0] . ":" . $auth["realm"] . ":" . $user[1] )));
   $ha2 = string.lowercase(string.hexencode(string.hashMD5( $method . ":" . $uri )));


   $cnonce = string.lowercase(string.hexencode(string.hashMD5( request.getRemoteIP() . $now )));

   $digest =  $ha1 .":". $auth["nonce"] .":". string.sprintf("%08d", $nc) .":". $cnonce .":auth:". $ha2;
   $digest = string.lowercase(string.hexencode(string.hashMD5($digest)));
   
   $header = "Digest username=\"" . $user[0] . "\", realm=\"" . $auth["realm"] . "\", nonce=\"" . 
             $auth["nonce"] . "\", uri=\"" . $uri . "\", response=\"" . $digest . "\"" .
             ", cnonce=\"" . $cnonce . "\", qop=auth, nc=" . string.sprintf("%08d", $nc) . 
             ", algorithm=MD5"; 

   return $header;

}


if ( rule.getstate() == "REQUEST" ) {
   parseRequest();
} else {
   parseResponse();

}

 

The rule should be applied to the Virtual Server as both a Request and a Response rule. It will automatically intercept any requests for Digest authentication from the server and convert them to/from Basic authentication.

 

Enjoy!

Version history
Revision #:
1 of 1
Last update:
‎06-03-2013 05:54:AM
Updated by: