http php digest,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 "

Hello {$_SERVER['PHP_AUTH_USER']}.

";

echo "

You entered {$_SERVER['PHP_AUTH_PW']} as your password.

";

}

// 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 的时候会再做说明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要用C语言实现HTTP Digest登录,需要进行以下步骤: 1. 创建HTTP Digest请求头部。HTTP Digest登录需要在请求头部添加Authorization字段,其中包含了用户名、密码、请求方法、URI等信息的摘要。要创建这个请求头部,可以使用OpenSSL库中的md5函数来计算摘要。 2. 发送HTTP请求。可以使用C语言的socket库来建立连接并发送HTTP请求。在发送请求时,需要将创建的请求头部添加到HTTP请求的头部中。 3. 接收HTTP响应。使用socket库从服务器接收HTTP响应,然后对响应进行解析,判断是否登录成功。 以下是一个简单的C语言HTTP Digest登录的代码示例: ```c #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <openssl/md5.h> // HTTP请求头部中的Authorization字段 void make_http_digest_auth(char *username, char *password, char *method, char *uri, char *realm, char *nonce, char *cnonce, char *qop, char *nc, char *digest) { char ha1[MD5_DIGEST_LENGTH]; char ha2[MD5_DIGEST_LENGTH]; char response[MD5_DIGEST_LENGTH]; // 计算HA1 char ha1_str[1024]; snprintf(ha1_str, sizeof(ha1_str), "%s:%s:%s", username, realm, password); MD5((unsigned char*)ha1_str, strlen(ha1_str), (unsigned char*)ha1); // 计算HA2 char ha2_str[1024]; snprintf(ha2_str, sizeof(ha2_str), "%s:%s", method, uri); MD5((unsigned char*)ha2_str, strlen(ha2_str), (unsigned char*)ha2); // 计算response char response_str[1024]; snprintf(response_str, sizeof(response_str), "%s:%s:%s:%s:%s:%s", ha1, nonce, nc, cnonce, qop, ha2); MD5((unsigned char*)response_str, strlen(response_str), (unsigned char*)response); // 组装Authorization字段 snprintf(digest, 1024, "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", cnonce=\"%s\", nc=%s, qop=%s", username, realm, nonce, uri, response, cnonce, nc, qop); } int main() { int sockfd; struct sockaddr_in servaddr; // 创建socket sockfd = socket(AF_INET, SOCK_STREAM, 0); // 设置服务器地址 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(80); inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); // 连接服务器 connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); // 创建HTTP Digest请求头部 char digest[1024]; char *username = "user"; char *password = "password"; char *method = "GET"; char *uri = "/"; char *realm = "test"; char *nonce = "123456"; char *cnonce = "654321"; char *qop = "auth"; char *nc = "00000001"; make_http_digest_auth(username, password, method, uri, realm, nonce, cnonce, qop, nc, digest); // 发送HTTP请求 char request[1024]; snprintf(request, 1024, "GET / HTTP/1.1\r\nHost: localhost\r\nAuthorization: %s\r\n\r\n", digest); write(sockfd, request, strlen(request)); // 接收HTTP响应 char response[1024]; int len = read(sockfd, response, 1024); response[len] = '\0'; printf("%s", response); // 关闭socket close(sockfd); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值