forked from webstudio-ltd/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaccess_guard.php
More file actions
151 lines (122 loc) · 5.05 KB
/
Copy pathaccess_guard.php
File metadata and controls
151 lines (122 loc) · 5.05 KB
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<?php
if (defined('WEBSTUDIO_DOCS_ACCESS_GUARD_LOADED')) {
return;
}
define('WEBSTUDIO_DOCS_ACCESS_GUARD_LOADED', true);
// Short, stable hash of the install directory so multiple instances on the
// same domain (different subdirectories) don't share cookies/sessions.
define('DOCS_INSTANCE_HASH', substr(hash('sha256', __DIR__), 0, 8));
if (PHP_SAPI === 'cli') {
return;
}
define('DOCS_ACCESS_COOKIE_NAME', 'docs_access_pass_' . DOCS_INSTANCE_HASH);
define('DOCS_ACCESS_COOKIE_TTL', 3600 * 24 * 30);
define('DOCS_ACCESS_KEY_FILE', __DIR__ . '/data/access_key.txt');
define('DOCS_ACCESS_QUERY_PARAM', 'token');
function docsIsHttpsRequest() {
if (!empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off') {
return true;
}
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
$forwardedProto = strtolower(trim(explode(',', (string)$_SERVER['HTTP_X_FORWARDED_PROTO'])[0]));
return $forwardedProto === 'https';
}
return false;
}
function docsCookieSameSite() {
// Cross-site iframe cookies require SameSite=None and Secure.
return docsIsHttpsRequest() ? 'None' : 'Lax';
}
function docsAccessReadConfig() {
if (!is_file(DOCS_ACCESS_KEY_FILE)) {
return ['enabled' => false];
}
if (!is_readable(DOCS_ACCESS_KEY_FILE)) {
return ['error' => 'unreadable'];
}
$raw = file_get_contents(DOCS_ACCESS_KEY_FILE);
if ($raw === false) {
return ['error' => 'unreadable'];
}
$lines = preg_split('/\r\n|\r|\n/', $raw) ?: [];
$entry = '';
foreach ($lines as $line) {
$line = trim($line);
if ($line === '' || (isset($line[0]) && $line[0] === '#')) {
continue;
}
$entry = $line;
break;
}
if ($entry === '') {
return ['enabled' => false];
}
return ['enabled' => true, 'value' => $entry];
}
function docsAccessCookieOptions($expires) {
return [
'expires' => $expires,
'path' => '/',
'secure' => docsIsHttpsRequest(),
'httponly' => true,
'samesite' => docsCookieSameSite(),
];
}
function docsAccessSetCookieValue($value, $expires) {
setcookie(DOCS_ACCESS_COOKIE_NAME, $value, docsAccessCookieOptions($expires));
if ($expires < time()) {
unset($_COOKIE[DOCS_ACCESS_COOKIE_NAME]);
return;
}
$_COOKIE[DOCS_ACCESS_COOKIE_NAME] = $value;
}
function docsAccessExpectedCookie($accessValue) {
return hash('sha256', 'webstudio-docs|' . $accessValue);
}
function docsAccessCurrentUrlWithoutParam($param) {
$path = '/';
if (!empty($_SERVER['REQUEST_URI'])) {
$requestPath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if (is_string($requestPath) && $requestPath !== '') {
$path = $requestPath;
}
} elseif (!empty($_SERVER['PHP_SELF'])) {
$path = $_SERVER['PHP_SELF'];
}
$query = $_GET;
unset($query[$param]);
if (!$query) {
return $path;
}
return $path . '?' . http_build_query($query);
}
function docsAccessDeny($message) {
http_response_code(403);
header('Content-Type: text/html; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
header('X-Robots-Tag: noindex, nofollow, noarchive');
echo '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Access denied</title><style>body{margin:0;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#111827;color:#f9fafb;display:flex;min-height:100vh;align-items:center;justify-content:center;padding:24px}.card{max-width:560px;background:#1f2937;border:1px solid #374151;border-radius:16px;padding:28px;box-shadow:0 24px 60px rgba(0,0,0,.35)}h1{margin:0 0 12px;font-size:28px}p{margin:0;color:#d1d5db;line-height:1.6}</style></head><body><main class="card"><h1>Access denied</h1><p>' . htmlspecialchars($message, ENT_QUOTES, 'UTF-8') . '</p></main></body></html>';
exit;
}
$docsAccessConfig = docsAccessReadConfig();
if (!empty($docsAccessConfig['error'])) {
docsAccessSetCookieValue('', time() - 3600);
docsAccessDeny('Access is not configured yet. Add your access key to data/access_key.txt first.');
}
if (empty($docsAccessConfig['enabled'])) {
return;
}
header('X-Robots-Tag: noindex, nofollow, noarchive');
$docsAccessValue = $docsAccessConfig['value'];
$docsAccessExpectedCookie = docsAccessExpectedCookie($docsAccessValue);
$docsAccessRequestValue = $_GET[DOCS_ACCESS_QUERY_PARAM] ?? null;
if (is_string($docsAccessRequestValue) && hash_equals($docsAccessValue, $docsAccessRequestValue)) {
docsAccessSetCookieValue($docsAccessExpectedCookie, time() + DOCS_ACCESS_COOKIE_TTL);
return;
}
$docsAccessCookie = $_COOKIE[DOCS_ACCESS_COOKIE_NAME] ?? '';
if (!is_string($docsAccessCookie) || !hash_equals($docsAccessExpectedCookie, $docsAccessCookie)) {
docsAccessSetCookieValue('', time() - 3600);
docsAccessDeny('Open this page once with the configured access link to unlock the content in this browser.');
}