aboutsummaryrefslogtreecommitdiff
path: root/http/bcrypt.php
blob: 00eda33ea3cebce69dec20ace9cdf49aad218f1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?php

const BCRYPT_COST = 12;

$user = $_SERVER['PHP_AUTH_USER'] ?? null;
$pass = $_SERVER['PHP_AUTH_PW']   ?? null;

// Fallback for CGI / FastCGI SAPIs that don't populate PHP_AUTH_*.
// Apache: add "CGIPassAuth On" (2.4.13+) or a RewriteRule to expose
// HTTP_AUTHORIZATION; nginx+php-fpm passes it through by default.
if ($user === null || $pass === null) {
    $header = $_SERVER['HTTP_AUTHORIZATION']
        ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
        ?? null;

    if (is_string($header) && preg_match('/^Basic\s+(.+)$/i', $header, $m) === 1) {
        $decoded = base64_decode($m[1], true);

        // Split on the FIRST colon only: passwords may contain colons.
        if ($decoded !== false && str_contains($decoded, ':')) {
            [$user, $pass] = explode(':', $decoded, 2);
        }
    }
}

if (!is_string($user) || $user === '' || !is_string($pass) || $pass === '') {
    header('WWW-Authenticate: Basic realm="Restricted", charset="UTF-8"');
    http_response_code(401);
    header('Content-Type: text/plain; charset=utf-8');
    header('Cache-Control: no-store');
    echo 'authentication required';
    exit;
}

if (strlen($pass) > 72) {
    http_response_code(422);
    header('Content-Type: text/plain; charset=utf-8');
    header('Cache-Control: no-store');
    echo 'password exceeds the 72-byte bcrypt limit';
    exit;
}

$hash = password_hash($pass, PASSWORD_BCRYPT, ['cost' => BCRYPT_COST]);

if ($hash === false) {
    http_response_code(500);
    header('Content-Type: text/plain; charset=utf-8');
    header('Cache-Control: no-store');
    echo 'hashing failed';
    exit;
}

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');

echo json_encode([
    'user'      => $user,
    'pass_hash' => $hash,
    'algo'      => password_get_info($hash)['algoName'],
    'cost'      => BCRYPT_COST,
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), "\n";