DooDigestAuth.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <?php
  2. /**
  3. * DooDigestAuth class file.
  4. *
  5. * @author Leng Sheng Hong <darkredz@gmail.com>
  6. * @link http://www.doophp.com/
  7. * @copyright Copyright &copy; 2009 Leng Sheng Hong
  8. * @license http://www.doophp.com/license
  9. */
  10. /**
  11. * Handles HTTP digest authentication
  12. *
  13. * <p>HTTP digest authentication can be used with the URI router.
  14. * HTTP digest is much more recommended over the use of HTTP Basic auth which doesn't provide any encryption.
  15. * If you are running PHP on Apache in CGI/FastCGI mode, you would need to
  16. * add the following line to your .htaccess for digest auth to work correctly.</p>
  17. * <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code>
  18. *
  19. * <p>This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.</p>
  20. *
  21. * @author Leng Sheng Hong <darkredz@gmail.com>
  22. * @version $Id: DooDigestAuth.php 1000 2009-07-7 18:27:22
  23. * @package doo.auth
  24. * @since 1.0
  25. */
  26. class DooDigestAuth{
  27. /**
  28. * Authenticate against a list of username and passwords.
  29. *
  30. * <p>HTTP Digest Authentication doesn't work with PHP in CGI mode,
  31. * you have to add this into your .htaccess <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code></p>
  32. *
  33. * @param string $realm Name of the authentication session
  34. * @param array $users An assoc array of username and password: array('uname1'=>'pwd1', 'uname2'=>'pwd2')
  35. * @param string $fail_msg Message to be displayed if the User cancel the login
  36. * @param string $fail_url URL to be redirect if the User cancel the login
  37. * @return string The username if login success.
  38. */
  39. public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){
  40. $realm = "Restricted area - $realm";
  41. //user => password
  42. //$users = array('admin' => '1234', 'guest' => 'guest');
  43. if(!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Digest')===0){
  44. $_SERVER['PHP_AUTH_DIGEST'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
  45. }
  46. if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
  47. header('WWW-Authenticate: Digest realm="'.$realm.
  48. '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
  49. header('HTTP/1.1 401 Unauthorized');
  50. if($fail_msg!=NULL)
  51. die($fail_msg);
  52. if($fail_url!=NULL)
  53. die("<script>window.location.href = '$fail_url'</script>");
  54. exit;
  55. }
  56. // analyze the PHP_AUTH_DIGEST variable
  57. if (!($data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])){
  58. header('WWW-Authenticate: Digest realm="'.$realm.
  59. '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
  60. header('HTTP/1.1 401 Unauthorized');
  61. if($fail_msg!=NULL)
  62. die($fail_msg);
  63. if($fail_url!=NULL)
  64. die("<script>window.location.href = '$fail_url'</script>");
  65. exit;
  66. }
  67. // generate the valid response
  68. $A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
  69. $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
  70. $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
  71. if ($data['response'] != $valid_response){
  72. header('HTTP/1.1 401 Unauthorized');
  73. header('WWW-Authenticate: Digest realm="'.$realm.
  74. '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
  75. if($fail_msg!=NULL)
  76. die($fail_msg);
  77. if($fail_url!=NULL)
  78. die("<script>window.location.href = '$fail_url'</script>");
  79. exit;
  80. }
  81. // ok, valid username & password
  82. return $data['username'];
  83. }
  84. /**
  85. * Method to parse the http auth header, works with IE.
  86. *
  87. * Internet Explorer returns a qop="xxxxxxxxxxx" in the header instead of qop=xxxxxxxxxxx as most browsers do.
  88. *
  89. * @param string $txt header string to parse
  90. * @return array An assoc array of the digest auth session
  91. */
  92. private static function http_digest_parse($txt)
  93. {
  94. $res = preg_match("/username=\"([^\"]+)\"/i", $txt, $match);
  95. $data['username'] = (isset($match[1]))?$match[1]:null;
  96. $res = preg_match('/nonce=\"([^\"]+)\"/i', $txt, $match);
  97. $data['nonce'] = $match[1];
  98. $res = preg_match('/nc=([0-9]+)/i', $txt, $match);
  99. $data['nc'] = $match[1];
  100. $res = preg_match('/cnonce=\"([^\"]+)\"/i', $txt, $match);
  101. $data['cnonce'] = $match[1];
  102. $res = preg_match('/qop=([^,]+)/i', $txt, $match);
  103. $data['qop'] = str_replace('"','',$match[1]);
  104. $res = preg_match('/uri=\"([^\"]+)\"/i', $txt, $match);
  105. $data['uri'] = $match[1];
  106. $res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match);
  107. $data['response'] = $match[1];
  108. return $data;
  109. }
  110. }