PHP的HTTP验证

PHP的HTTP验证

在日常开发中,我们进行用户登录的时候,大部分情况下都会使用 session 来保存用户登录信息,并以此为依据判断用户是否已登录。但其实 HTTP 也提供了这种登录验证机制,我们今天就来学习关于 HTTP 验证相关的知识。

HTTP Basic

if (!isset($_SERVER['PHP_AUTH_USER'])) {
    header('WWW-Authenticate: Basic realm="My Realm"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Text to send if user hits Cancel button';
    exit;
} else {
    echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
    echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
}
// Authorization: Basic YWFhOmFhYQ==
echo base64_decode('YWFhOmFhYQ==');
// aaa:aaa 等于明文

还是直接就从代码入手,上面的代码就是最简单的一种 HTTP 认证方式,如果 $_SERVER['PHP_AUTH_USER'] 不存在,那么我们就向浏览器发送一个 401 响应头,就是告诉浏览器我们需要登录验证。当浏览器收到这个响应头时,就会弹出一个浏览器自带的验证框并要求输入用户名和密码。

当我们填写了用户名和密码后,浏览器会在请求头中带上 Authorization 字段,并且将 base64 之后的用户名和密码发送过来。同时,PHP将会分别把用户名和密码解析到 \$_SERVER['PHP_AUTH_USER'] 和 $_SERVER['PHP_AUTH_PW'] 中。

上述这种认证方式就是最简单的 HTTP Basic 认证,可以看出,这种方式进行验证的用户名和密码其实是相当于明文传输的,因为 base64 很容易就可以反向解析出来。所以这种方式是非常不安全的。那么有没有更复杂一些的方式呢?

HTTP Digest

既然这么写了,那肯定是有更好的方式啦,那就是 HTTP Digest 方式的 HTTP 认证。

$realm = 'Restricted area';

//user => password
$users = array('admin' => 'mypass', 'guest' => 'guest');

// 指定 Digest 验证方式
if (empty($_SERVER['PHP_AUTH_DIGEST']) || !$_COOKIE['login']) {
    setcookie('login', 1);  // 退出登录条件判断
    header('HTTP/1.1 401 Unauthorized');
    header('WWW-Authenticate: Digest realm="' . $realm .
        '",qop="auth",nonce="' . uniqid() . '",opaque="' . md5($realm) . '"');
    
    // 如果用户不输入密码点了取消
    die('您点了取消,无法登录');
    
}

// 验证用户登录信息
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
    !isset($users[$data['username']])) {
    die('Wrong Credentials!');
}

// 验证登录信息
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
$valid_response = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
// $data['response'] 是浏览器客户端的加密内容
if ($data['response'] != $valid_response) {
    die('Wrong Credentials!');
}

// 用户名密码验证成功
echo '您的登录用户为: ' . $data['username'];
setcookie("login", 2);

// Authorization: Digest username="guest", realm="Restricted area", nonce="5e815bcbb4eba", uri="/", response="9286ea8d0fac79d3a95fff3e442d6d79", opaque="cdce8a5c95a1427d74df7acbf41c9ce0", qop=auth, nc=00000002, cnonce="a42e137359673851"
// 服务器回复报文中的nonce值,加上username,password, http method, http uri利用MD5(或者服务器指定的其他算法)计算出request-digest,作为repsonse头域的值

// 获取登录信息
function http_digest_parse($txt)
{
    // echo $txt;
    // protect against missing data
    $needed_parts = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
    $data = array();
    $keys = implode('|', array_keys($needed_parts));

preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);

foreach ($matches as $m) {
        $data[$m[1]] = $m[3] ? $m[3] : $m[4];
        unset($needed_parts[$m[1]]);
    }

return $needed_parts ? false : $data;
}

if($_GET['logout']){

setcookie("login", 0);
    header("Location: /");
}

从代码量就可以看出这种方式复杂了很多。首先是我们一样需要在未登录的情况下返回 401 响应头,告诉浏览器我们要进行 Digest 认证。这里 header 信息就有不一样的地方了,格式是 Digest ,内容也比 Basic 多了许多,这些多出来的内容都是我们在验证认证内容的时候需要用到的。

接着,浏览器一样是会弹出输入用户名和密码的弹窗。然后将加密后的用户名和密码信息提交上来。我们可以看到返回值里有明文的 username ,但是没有明文的密码了。其实密码是通过 username 、 密码 、 nonce 、 nc 、 cnoce 、cop 、$_SERVER['REQUEST_METHOD'] 、 uri 等这些内容进行 md5 加密后生成的,放在了 response 字段中提交了上来。我们也需要按照同样的规则获得加密后的密码进行比对就可以判定用户名和密码正确从而让用户完成正常的登录流程。

在这段代码中,我们加入了一个 cookie ,是为了做退出登录的判断使用的。因为 HTTP 认证这种形式的过期时间是以浏览器为基准的。也就是如果客户端关闭了浏览器,则客户端浏览器内存中保存的用户名和密码才会消失。这种情况下我们只能通过 cookie 来进行退出登录的操作,如果用户退出登录了就改变这个 cookie 的内容并重新发送 401 响应头给浏览要求重新登录。

总结

HTTP 验证的这种操作一般不会做为我们日常开发中的正常登录功能,大部分情况下,我们会给后台或者一些特殊的管理工具加上一层这种 HTTP 认证来实现双重的认证,也就是为了保障后台的数据安全。比如,我会在我的 phpMyAdmin 上增加一层这个认证。另外,HTTP 认证也可以直接在 Nginx 或 Apache 中直接配置,不需要走到 PHP 这一层来,这个我们将来学习 Nginx 的时候会再做说明。

测试代码:https://github.com/zhangyue0503/dev-blog/blob/master/php/202003/source/PHP%E7%9A%84HTTP%E9%AA%8C%E8%AF%81.php

参考文档:https://www.php.net/manual/zh/features.http-auth.php

(0)

相关推荐

  • 配置phpmyadmin的认证方式

    phpmyadmin提供了 三种方式: config / http / cookie http和cookie这两种方式,都提示用户输入用户名和密码, 而且, 一旦开了一个窗口 认证成功 , 再开一个窗 ...

  • Thinkphp 表单验证规则

    'number'      => ':attribute必须是数字', 'integer'     => ':attribute必须是整数', 'float'       => ': ...

  • 分时小技巧的验证

    从我的自选股里来看看几只个股,起码对上次说的高抛低吸点来一个对照.1,川能动力,弱势开盘,第一波反弹站上轴线,这是一个成功的上升.9:45下杀,低于开盘价,这就决定了今天大概率不会走强.9:48第二波 ...

  • 八字秘诀|通过六亲验证八字中的喜用神!

    看一个人的八字,大多就是要先确定这个八字的喜用神.不然则无法进一步推衍出你人生中即将发生的事件以及即将遭遇的人物会对你产生怎样的利与害关系.有的八字,喜用神极易判断,一看则比较明了:而有的八字则就不那 ...

  • 基于system verilog的图像处理验证平台 bmp文件解析

    版权所有:转载请注明 https://blog.csdn.net/jayash/article/details/79947314 基于FPGA的图像处理中,rtl代码的仿真验证一直是重中之重, 之前也 ...

  • 【每日一题】(1351)这个实验想验证什么?

    每日一题 进步一点 (2020.12衢州模拟) 将两块相同的土样按野外土壤结构填满装置,在其中一块土样上方种植草皮,装置底部 有渗水孔,装置上方设有淋蓬头,可通过"雨水均匀分布器" ...

  • 我家祖传秘方-根除肝硬化(肝癌) 隐世医术,千人验证一次即效!(奇方..

    治疗肝硬化几乎百分之百治愈,十分灵验. 方一,金银花,没药,乳香,泡三甲,皂角刺,花粉,贝母,牛子,连翘,甘草节.(仙方活命饮加减) 方二, 姜黄,积壳,姜制川朴,苍术,金银花,(理气) 方三,姜制川 ...

  • 补充胆碱可减轻阿尔茨海默症?科学验证饮食方案如何抵御大脑老化

    [文献解读] 2019年8月美国亚利桑那州立大学研究团队在期刊<Aging Cell>中发表文章,探究终生有规律的补充胆碱是否能减轻阿尔兹海默症患者(AD)病理和相关的空间参考记忆缺陷.结 ...

  • PPT分享-工艺验证和及清洁验证

    <课程推荐>杭州<药品上市后变更管理办法>全面解析及企业贯彻实施专题培训班5月28日杭州-第四期2021cGMP新厂建设项目管理专题培训班5月28日[杭州-成都]药品研发分析和 ...

  • 临床验证十分有效的中医神剂中医经验方

    什么病哪几味主药不能少. 腰椎骨质增生灵仙,骨碎补,补骨脂,不能少: 椎病葛根,桑枝,片姜黄,不能少: 腰椎盘突出马前子,乌头不能少. 风湿性关节炎独活寄生汤加减就可以了 类风关雷公藤,青风腾,尪痹冲 ...

  • 两位武学大家验证:太极拳速如放箭,拳从心出打中线

    <太极拳十三势行功心解>告诉我们什么(三).本文首发于今日头条之允升园. 武氏太极拳宗师武禹襄所著.杨氏太极拳宗师所注解的<十三势行功心解>,真的是博大深奥.妙趣横生. 前段时 ...