PluginProbe
HTTP Headers / 1.18.8
HTTP Headers v1.18.8
1.19.5 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.12.0 1.12.1 1.12.2 1.13.0 1.13.1 1.13.2 1.13.3 1.13.4 1.14.0 1.14.1 1.14.2 1.15.0 All 60 releases
http-headers / http-headers.php

http-headers.php in HTTP Headers 1.18.8, at http-headers.php

1,702 lines 59.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: HTTP Headers
4 Plugin URI: https://github.com/riverside/http-headers
5 Description: A plugin for HTTP headers management including security, access-control (CORS), caching, compression, and authentication.
6 Version: 1.18.8
7 Author: Dimitar Ivanov
8 Author URI: https://github.com/riverside
9 License: GPLv2 or later
10 Text Domain: http-headers
11 */
12
13 /*
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License
16 as published by the Free Software Foundation; either version 2
17 of the License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/copyleft/gpl.html>.
26
27 Copyright (c) 2017-2023 Dimitar Ivanov
28 */
29
30 if (!defined('ABSPATH')) {
31 exit;
32 }
33
34 $options = include dirname(__FILE__) . '/views/includes/options.inc.php';
35 foreach ($options as $option) {
36 if (get_option($option[0]) === false) {
37 add_option($option[0], $option[1], null, 'yes');
38 }
39 }
40
41 function build_csp_value($value) {
42 if (!is_array($value))
43 {
44 return NULL;
45 }
46 $csp = array();
47 foreach ($value as $key => $val)
48 {
49 if (is_array($val))
50 {
51 $source = NULL;
52 if (isset($val['source']))
53 {
54 $source = $val['source'];
55 unset($val['source']);
56 }
57 if (!empty($val))
58 {
59 $val = join(" ", array_keys($val));
60 if ($source)
61 {
62 $val .= " " . $source;
63 }
64 $csp[] = sprintf("%s %s", $key, $val);
65 } elseif ($source) {
66 $csp[] = sprintf("%s %s", $key, $source);
67 }
68 } else {
69 if (in_array($key, array('block-all-mixed-content', 'upgrade-insecure-requests')))
70 {
71 $csp[] = $key;
72 }
73 if (in_array($key, array('plugin-types', 'report-to')) && !empty($val))
74 {
75 $csp[] = sprintf("%s %s", $key, $val);
76 }
77 }
78 }
79
80 if (!$csp)
81 {
82 return NULL;
83 }
84
85 return join('; ', $csp);
86 }
87
88 function get_htaccess_filename() {
89 return get_option('hh_htaccess_path');
90 }
91
92 function get_user_ini_filename() {
93 return get_option('hh_user_ini_path');
94 }
95
96 function get_htpasswd_filename() {
97 return get_option('hh_htpasswd_path');
98 }
99
100 function get_htdigest_filename() {
101 return get_option('hh_htdigest_path');
102 }
103
104 function get_http_headers() {
105 $statuses = array();
106 $unset = array();
107 $headers = array();
108 $append = array();
109 if (get_option('hh_x_frame_options') == 1) {
110 $x_frame_options_value = strtoupper(get_option('hh_x_frame_options_value'));
111 if ($x_frame_options_value == 'ALLOW-FROM') {
112 $x_frame_options_value .= ' ' . get_option('hh_x_frame_options_domain');
113 }
114 $headers['X-Frame-Options'] = $x_frame_options_value;
115 }
116 if (get_option('hh_x_powered_by') == 1) {
117 if (get_option('hh_x_powered_by_option') == 'set') {
118 $headers['X-Powered-By'] = get_option('hh_x_powered_by_value');
119 } else {
120 $unset[] = 'X-Powered-By';
121 }
122 }
123 if (get_option('hh_x_xxs_protection') == 1) {
124 $headers['X-XSS-Protection'] = get_option('hh_x_xxs_protection_value');
125 if ($headers['X-XSS-Protection'] == '1; report=') {
126 $headers['X-XSS-Protection'] .= get_option('hh_x_xxs_protection_uri');
127 }
128 }
129 if (get_option('hh_x_content_type_options') == 1) {
130 $headers['X-Content-Type-Options'] = get_option('hh_x_content_type_options_value');
131 }
132 if (get_option('hh_x_download_options') == 1) {
133 $headers['X-Download-Options'] = get_option('hh_x_download_options_value');
134 }
135 if (get_option('hh_x_permitted_cross_domain_policies') == 1) {
136 $headers['X-Permitted-Cross-Domain-Policies'] = get_option('hh_x_permitted_cross_domain_policies_value');
137 }
138 if (get_option('hh_x_dns_prefetch_control') == 1) {
139 $headers['X-DNS-Prefetch-Control'] = get_option('hh_x_dns_prefetch_control_value');
140 }
141 if (get_option('hh_connection') == 1) {
142 $headers['Connection'] = get_option('hh_connection_value');
143 }
144 if (get_option('hh_pragma') == 1) {
145 $headers['Pragma'] = get_option('hh_pragma_value');
146 }
147 if (get_option('hh_age') == 1) {
148 $headers['Age'] = sprintf("%u", get_option('hh_age_value'));
149 }
150 if (get_option('hh_cache_control') == 1) {
151 $hh_cache_control_value = get_option('hh_cache_control_value', array());
152 $tmp = array();
153 foreach ($hh_cache_control_value as $k => $v) {
154 if (in_array($k, array('max-age', 's-maxage', 'stale-while-revalidate', 'stale-if-error'))) {
155 if (strlen($v) > 0) {
156 $tmp[] = sprintf("%s=%u", $k, $v);
157 }
158 } else {
159 $tmp[] = $k;
160 }
161 }
162 $hh_cache_control_value = join(', ', $tmp);
163 $headers['Cache-Control'] = $hh_cache_control_value;
164 }
165 if (get_option('hh_strict_transport_security') == 1) {
166 $hh_strict_transport_security = array();
167
168 $hh_strict_transport_security_max_age = get_option('hh_strict_transport_security_max_age');
169 if ($hh_strict_transport_security_max_age !== false)
170 {
171 $hh_strict_transport_security[] = sprintf('max-age=%u', get_option('hh_strict_transport_security_max_age'));
172 if (get_option('hh_strict_transport_security_sub_domains'))
173 {
174 $hh_strict_transport_security[] = 'includeSubDomains';
175 }
176 if (get_option('hh_strict_transport_security_preload'))
177 {
178 $hh_strict_transport_security[] = 'preload';
179 }
180 } else {
181 $hh_strict_transport_security = array(get_option('hh_strict_transport_security_value'));
182 }
183 $headers['Strict-Transport-Security'] = join('; ', $hh_strict_transport_security);
184 }
185 if (get_option('hh_x_ua_compatible') == 1) {
186 $headers['X-UA-Compatible'] = get_option('hh_x_ua_compatible_value');
187 }
188
189 if (get_option('hh_content_security_policy') == 1)
190 {
191 $value = get_option('hh_content_security_policy_value');
192 $csp = build_csp_value($value);
193 if ($csp)
194 {
195 $csp_report_only = get_option('hh_content_security_policy_report_only');
196 $headers['Content-Security-Policy'.($csp_report_only ? '-Report-Only' : NULL)] = $csp;
197 }
198 }
199
200 if (get_option('hh_access_control_allow_origin') == 1)
201 {
202 $value = get_option('hh_access_control_allow_origin_value');
203 switch ($value)
204 {
205 case 'origin':
206 $value = get_option('hh_access_control_allow_origin_url', array());
207 if (is_scalar($value))
208 {
209 $value = array($value);
210 }
211 break;
212 }
213 if (!empty($value))
214 {
215 $headers['Access-Control-Allow-Origin'] = $value;
216 }
217 }
218 if (get_option('hh_access_control_allow_credentials') == 1)
219 {
220 $headers['Access-Control-Allow-Credentials'] = get_option('hh_access_control_allow_credentials_value');
221 }
222 if (get_option('hh_access_control_max_age') == 1)
223 {
224 $value = get_option('hh_access_control_max_age_value');
225 if (!empty($value))
226 {
227 $headers['Access-Control-Max-Age'] = intval($value);
228 }
229 }
230 if (get_option('hh_access_control_allow_methods') == 1)
231 {
232 $value = get_option('hh_access_control_allow_methods_value');
233 if (!empty($value))
234 {
235 $headers['Access-Control-Allow-Methods'] = join(', ', array_keys($value));
236 }
237 }
238 if (get_option('hh_access_control_allow_headers') == 1)
239 {
240 $tmp = array();
241 $value = get_option('hh_access_control_allow_headers_value');
242 if (!empty($value))
243 {
244 $tmp = array_merge($tmp, array_keys($value));
245 }
246 $custom = get_option('hh_access_control_allow_headers_custom');
247 if (!empty($custom))
248 {
249 $tmp = array_merge($tmp, $custom);
250 }
251 if ($tmp)
252 {
253 $tmp = array_filter($tmp, 'trim');
254 $tmp = array_unique($tmp);
255 $headers['Access-Control-Allow-Headers'] = join(', ', $tmp);
256 }
257 }
258 if (get_option('hh_access_control_expose_headers') == 1)
259 {
260 $tmp = array();
261 $value = get_option('hh_access_control_expose_headers_value');
262 if (!empty($value))
263 {
264 $tmp = array_merge($tmp, array_keys($value));
265 }
266 $custom = get_option('hh_access_control_expose_headers_custom');
267 if (!empty($custom))
268 {
269 $tmp = array_merge($tmp, $custom);
270 }
271 if ($tmp)
272 {
273 $tmp = array_filter($tmp, 'trim');
274 $tmp = array_unique($tmp);
275 $headers['Access-Control-Expose-Headers'] = join(', ', $tmp);
276 }
277 }
278 if (get_option('hh_p3p') == 1)
279 {
280 $value = get_option('hh_p3p_value');
281 if (!empty($value))
282 {
283 $headers['P3P'] = 'CP="' . join(' ', array_keys($value)) . '"';
284 }
285 }
286 if (get_option('hh_referrer_policy') == 1) {
287 $headers['Referrer-Policy'] = get_option('hh_referrer_policy_value');
288 }
289 if (get_option('hh_cross_origin_resource_policy') == 1) {
290 $headers['Cross-Origin-Resource-Policy'] = get_option('hh_cross_origin_resource_policy_value');
291 }
292 if (get_option('hh_cross_origin_embedder_policy') == 1) {
293 $headers['Cross-Origin-Embedder-Policy'] = get_option('hh_cross_origin_embedder_policy_value');
294 }
295 if (get_option('hh_cross_origin_opener_policy') == 1) {
296 $headers['Cross-Origin-Opener-Policy'] = get_option('hh_cross_origin_opener_policy_value');
297 }
298 if (get_option('hh_www_authenticate') == 1) {
299
300 switch (get_option('hh_www_authenticate_type')) {
301 case 'Basic':
302 if (!(isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])
303 && $_SERVER['PHP_AUTH_USER'] == get_option('hh_www_authenticate_user')
304 && $_SERVER['PHP_AUTH_PW'] == get_option('hh_www_authenticate_pswd'))) {
305 $headers['WWW-Authenticate'] = sprintf("Basic realm='%s'", get_option('hh_www_authenticate_realm'));
306 $statuses['HTTP/1.1'] = '401 Unauthorized';
307 }
308 break;
309 case 'Digest':
310 if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
311 $realm = get_option('hh_www_authenticate_realm');
312 $headers['WWW-Authenticate'] = sprintf("Digest realm='%s',qop='auth',nonce='%s',opaque='%s'",
313 $realm, uniqid(), md5($realm));
314 $statuses['HTTP/1.1'] = '401 Unauthorized';
315 }
316 break;
317 }
318 }
319 if (get_option('hh_vary') == 1)
320 {
321 $value = get_option('hh_vary_value');
322 if (!empty($value))
323 {
324 $append['Vary'] = join(', ', array_keys($value));
325 }
326 }
327
328 if (get_option('hh_expect_ct') == 1) {
329 $expect_ct_max_age = get_option('hh_expect_ct_max_age');
330 $expect_ct_report_uri = get_option('hh_expect_ct_report_uri');
331 if (!empty($expect_ct_report_uri) && !empty($expect_ct_max_age)) {
332
333 $expect_ct = array();
334 $expect_ct[] = sprintf("max-age=%u", $expect_ct_max_age);
335 if (get_option('hh_expect_ct_enforce') == 1) {
336 $expect_ct[] = "enforce";
337 }
338 $expect_ct[] = sprintf('report-uri="%s"', $expect_ct_report_uri);
339 $headers['Expect-CT'] = join(', ', $expect_ct);
340 }
341 }
342 if (get_option('hh_custom_headers') == 1) {
343 $custom_headers = get_option('hh_custom_headers_value');
344 if (isset($custom_headers['name'], $custom_headers['value']) && !empty($custom_headers['name'])) {
345 foreach ($custom_headers['name'] as $key => $name) {
346 $name = trim($name);
347 $value = trim($custom_headers['value'][$key]);
348 if (empty($name) || empty($value)) {
349 continue;
350 }
351 $headers[$name] = $value;
352 }
353 }
354 }
355
356 $value = get_http_header('report_to');
357 if ($value) {
358 $headers['Report-To'] = $value;
359 }
360
361 $value = get_http_header('nel');
362 if ($value) {
363 $headers['NEL'] = $value;
364 }
365
366 $value = get_http_header('feature_policy');
367 if ($value) {
368 $headers['Feature-Policy'] = $value;
369 }
370
371 $value = get_http_header('permissions_policy');
372 if ($value) {
373 $headers['Permissions-Policy'] = $value;
374 }
375
376 $value = get_http_header('x_robots_tag');
377 if ($value) {
378 $headers['X-Robots-Tag'] = $value;
379 }
380
381 return array($headers, $statuses, $unset, $append);
382 }
383
384 function get_http_header($header_name) {
385 $fn = sprintf('get_%s_header', $header_name);
386 if (!function_exists($fn)) {
387 return NULL;
388 }
389
390 return call_user_func($fn);
391 }
392
393 function get_report_to_header() {
394 if (get_option('hh_report_to') != 1) {
395 return NULL;
396 }
397 $report_to = get_option('hh_report_to_value');
398 $tmp = array();
399 foreach ($report_to as $item) {
400 $endpoints = array();
401 foreach ($item['endpoints'] as $endpoint) {
402 $endpoints[] = sprintf('{"url": "%s"%s%s}',
403 $endpoint['url'],
404 is_numeric($endpoint['priority']) ? sprintf(', "priority": %u', $endpoint['priority']) : NULL,
405 is_numeric($endpoint['weight']) ? sprintf(', "weight": %u', $endpoint['weight']) : NULL
406 );
407 }
408
409 $tmp[] = sprintf('{"max_age": %u%s%s, "endpoints": [%s]}',
410 $item['max_age'],
411 $item['group'] ? sprintf(', "group": "%s"', $item['group']) : NULL,
412 $item['include_subdomains'] ? sprintf(', "include_subdomains": true') : NULL,
413 join(", ", $endpoints)
414 );
415 }
416
417 return join(', ', $tmp);
418 }
419
420 function get_x_robots_tag_header() {
421 if (get_option('hh_x_robots_tag') != 1) {
422 return NULL;
423 }
424
425 $hh_x_robots_tag_value = get_option('hh_x_robots_tag_value', array());
426 $tmp = array();
427 foreach ($hh_x_robots_tag_value as $k => $v) {
428 if ($k == 'max-snippet') {
429 if (is_numeric($v) && $v >= -1) {
430 $tmp[] = "$k:$v";
431 }
432 } elseif ($k == 'max-image-preview') {
433 if (!empty($v)) {
434 $tmp[] = "$k:$v";
435 }
436 } elseif ($k == 'max-video-preview') {
437 if (is_numeric($v) && $v >= -1) {
438 $tmp[] = "$k:$v";
439 }
440 } elseif ($k == 'unavailable_after') {
441 if (!empty($v)) {
442 $tmp[] = "$k:$v";
443 }
444 } else {
445 $tmp[] = $k;
446 }
447 }
448 return join(', ', $tmp);
449 }
450
451 function get_nel_header() {
452 if (get_option('hh_nel') != 1) {
453 return NULL;
454 }
455
456 $nel = get_option('hh_nel_value', array());
457 return sprintf('{"report_to": "%s", "max_age": %u%s%s%s%s%s}',
458 isset($nel['report_to']) ? $nel['report_to'] : NULL,
459 isset($nel['max_age']) ? $nel['max_age'] : NULL,
460 isset($nel['include_subdomains']) ? ', "include_subdomains": true' : NULL,
461 array_key_exists('success_fraction', $nel) && is_numeric($nel['success_fraction']) ? ', "success_fraction": '. $nel['success_fraction'] : NULL,
462 array_key_exists('failure_fraction', $nel) && is_numeric($nel['failure_fraction']) ? ', "failure_fraction": '. $nel['failure_fraction'] : NULL,
463 isset($nel['request_headers']) && !empty($nel['request_headers']) ? sprintf(', "request_headers": ["%s"]', join('", "', array_map('trim', explode(',', $nel['request_headers'])))) : NULL,
464 isset($nel['response_headers']) && !empty($nel['response_headers']) ? sprintf(', "response_headers": ["%s"]', join('", "', array_map('trim', explode(',', $nel['response_headers'])))) : NULL
465 );
466 }
467
468 function get_feature_policy_header() {
469 if (get_option('hh_feature_policy') != 1) {
470 return NULL;
471 }
472 $feature_policy_feature = get_option('hh_feature_policy_feature');
473 $feature_policy_value = get_option('hh_feature_policy_value');
474 $feature_policy_origin = get_option('hh_feature_policy_origin');
475 $tmp = array();
476 $feature_policy_feature = is_array($feature_policy_feature) ? $feature_policy_feature : array();
477 foreach (array_keys($feature_policy_feature) as $feature) {
478 $value = NULL;
479 switch ($feature_policy_value[$feature]) {
480 case '*':
481 case "'none'":
482 $value = $feature_policy_value[$feature];
483 break;
484 case "'self'":
485 $value = $feature_policy_value[$feature];
486 if (!empty($feature_policy_origin[$feature])) {
487 $value .= " " . $feature_policy_origin[$feature];
488 }
489 break;
490 case 'origin(s)':
491 $value = $feature_policy_origin[$feature];
492 break;
493 }
494
495 $tmp[] = sprintf("%s %s", $feature, $value);
496 }
497
498 return join('; ', $tmp);
499 }
500
501 function get_permissions_policy_header() {
502 if (get_option('hh_permissions_policy') != 1) {
503 return NULL;
504 }
505 $permissions_policy_feature = get_option('hh_permissions_policy_feature');
506 $permissions_policy_value = get_option('hh_permissions_policy_value');
507 $permissions_policy_origin = get_option('hh_permissions_policy_origin');
508
509 $tmp = array();
510 $permissions_policy_feature = is_array($permissions_policy_feature) ? $permissions_policy_feature : array();
511 foreach (array_keys($permissions_policy_feature) as $feature) {
512
513 $origins = NULL;
514 if (!empty($permissions_policy_origin[$feature]))
515 {
516 $origins = $permissions_policy_origin[$feature];
517 $origins = str_replace(array('"', "'"), '', $origins);
518 $origins = explode(' ', $origins);
519 $origins = array_filter($origins);
520 $origins = array_unique($origins);
521 $origins = '"' . join('" "', $origins) . '"';
522 }
523
524 $value = NULL;
525 switch ($permissions_policy_value[$feature]) {
526 case '*':
527 $value = '*';
528 break;
529 case "none":
530 $value = '()';
531 break;
532 case "self":
533 $value = 'self';
534 if ($origins)
535 {
536 $value .= ' ' . $origins;
537 }
538 $value = sprintf('(%s)', $value);
539 break;
540 case 'origin(s)':
541 $value = sprintf('(%s)', $origins);
542 break;
543 }
544
545 $tmp[] = sprintf('%s=%s', $feature, $value);
546 }
547
548 return join(', ', $tmp);
549 }
550
551 function http_digest_parse($txt) {
552 $txt = stripslashes($txt);
553
554 $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
555 $data = array();
556 $keys = implode('|', array_keys($needed_parts));
557
558 $matches = null;
559 preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
560
561 foreach ($matches as $m) {
562 $data[$m[1]] = $m[3] ? $m[3] : $m[4];
563 unset($needed_parts[$m[1]]);
564 }
565
566 return $needed_parts ? false : $data;
567 }
568
569 function php_auth_digest() {
570 if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || get_option('hh_www_authenticate_user') != $data['username']) {
571 die('Wrong Credentials!');
572 }
573
574 $A1 = md5($data['username'] . ':' . get_option('hh_www_authenticate_realm') . ':' . get_option('hh_www_authenticate_pswd'));
575 $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
576 $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
577 if ($data['response'] != $valid_response) {
578 die('Wrong Credentials!');
579 }
580 }
581
582 function php_content_encoding() {
583 if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) {
584 ob_start('ob_gzhandler');
585 } else {
586 ob_start();
587 }
588 }
589
590 function php_cookie_security_directives() {
591 $lines = array();
592 if (get_option('hh_cookie_security') == 1) {
593 $value = get_option('hh_cookie_security_value', array());
594 if (isset($value['HttpOnly'])) {
595 $lines[] = 'session.cookie_httponly = on';
596 }
597 if (isset($value['Secure'])) {
598 $lines[] = 'session.cookie_secure = on';
599 }
600 if (isset($value['SameSite']) && in_array($value['SameSite'], array('None', 'Lax', 'Strict'))) {
601 $lines[] = sprintf('session.cookie_samesite = "%s"', $value['SameSite']);
602 }
603 }
604
605 return $lines;
606 }
607
608 function http_headers() {
609 if (!is_php_mode()) {
610 return;
611 }
612 // PHP method below
613 list($headers, $statuses, $unset, $append) = get_http_headers();
614 $isCors = false;
615 foreach ($headers as $key => $value) {
616 if ($key == 'Access-Control-Allow-Origin') {
617 if (isset($_SERVER['HTTP_ORIGIN'])) {
618 if (in_array($value, array('*', 'null'))) {
619 $isCors = true;
620 header(sprintf("%s: *", $key));
621 }
622
623 if (is_array($value) && in_array($_SERVER['HTTP_ORIGIN'], $value)) {
624 $isCors = true;
625 header(sprintf("%s: %s", $key, $_SERVER['HTTP_ORIGIN']));
626 header("Vary: Origin", false);
627 }
628 }
629 continue;
630 }
631 if (in_array($key, array('Access-Control-Allow-Headers', 'Access-Control-Allow-Methods', 'Access-Control-Allow-Credentials', 'Access-Control-Max-Age', 'Access-Control-Expose-Headers'))) {
632 if ($isCors) {
633 header(sprintf("%s: %s", $key, $value));
634 }
635 continue;
636 }
637 header(sprintf("%s: %s", $key, $value));
638 }
639 foreach ($append as $key => $value) {
640 header(sprintf("%s: %s", $key, $value), false);
641 }
642 foreach ($unset as $header) {
643 if (function_exists('header_remove')) {
644 header_remove($header);
645 } else {
646 header("$header:");
647 }
648 }
649 foreach ($statuses as $key => $value) {
650 header(sprintf("%s %s", $key, $value));
651 exit;
652 }
653
654 if (get_option('hh_www_authenticate') == 1) {
655 php_auth_digest();
656 }
657
658 if (get_option('hh_content_encoding') == 1) {
659 php_content_encoding();
660 }
661 }
662
663 function http_headers_admin_add_page() {
664 add_options_page('HTTP Headers', 'HTTP Headers', 'manage_options', 'http-headers', 'http_headers_admin_page');
665 }
666
667 function http_headers_admin() {
668 register_setting('http-headers-mtd', 'hh_method');
669 register_setting('http-headers-mtd', 'hh_htaccess_path');
670 register_setting('http-headers-mtd', 'hh_user_ini_path');
671 register_setting('http-headers-mtd', 'hh_htpasswd_path');
672 register_setting('http-headers-mtd', 'hh_htdigest_path');
673 register_setting('http-headers-xfo', 'hh_x_frame_options');
674 register_setting('http-headers-xfo', 'hh_x_frame_options_value');
675 register_setting('http-headers-xfo', 'hh_x_frame_options_domain');
676 register_setting('http-headers-xss', 'hh_x_xxs_protection');
677 register_setting('http-headers-xss', 'hh_x_xxs_protection_value');
678 register_setting('http-headers-xss', 'hh_x_xxs_protection_uri');
679 register_setting('http-headers-cto', 'hh_x_content_type_options');
680 register_setting('http-headers-cto', 'hh_x_content_type_options_value');
681 register_setting('http-headers-sts', 'hh_strict_transport_security');
682 register_setting('http-headers-sts', 'hh_strict_transport_security_value'); //obsolete
683 register_setting('http-headers-sts', 'hh_strict_transport_security_max_age');
684 register_setting('http-headers-sts', 'hh_strict_transport_security_sub_domains');
685 register_setting('http-headers-sts', 'hh_strict_transport_security_preload');
686 register_setting('http-headers-uac', 'hh_x_ua_compatible');
687 register_setting('http-headers-uac', 'hh_x_ua_compatible_value');
688 register_setting('http-headers-p3p', 'hh_p3p');
689 register_setting('http-headers-p3p', 'hh_p3p_value');
690 register_setting('http-headers-rp', 'hh_referrer_policy');
691 register_setting('http-headers-rp', 'hh_referrer_policy_value');
692 register_setting('http-headers-csp', 'hh_content_security_policy');
693 register_setting('http-headers-csp', 'hh_content_security_policy_value');
694 register_setting('http-headers-csp', 'hh_content_security_policy_report_only');
695 register_setting('http-headers-acao', 'hh_access_control_allow_origin');
696 register_setting('http-headers-acao', 'hh_access_control_allow_origin_value');
697 register_setting('http-headers-acao', 'hh_access_control_allow_origin_url');
698 register_setting('http-headers-acac', 'hh_access_control_allow_credentials');
699 register_setting('http-headers-acac', 'hh_access_control_allow_credentials_value');
700 register_setting('http-headers-acam', 'hh_access_control_allow_methods');
701 register_setting('http-headers-acam', 'hh_access_control_allow_methods_value');
702 register_setting('http-headers-acah', 'hh_access_control_allow_headers');
703 register_setting('http-headers-acah', 'hh_access_control_allow_headers_value');
704 register_setting('http-headers-acah', 'hh_access_control_allow_headers_custom');
705 register_setting('http-headers-aceh', 'hh_access_control_expose_headers');
706 register_setting('http-headers-aceh', 'hh_access_control_expose_headers_value');
707 register_setting('http-headers-aceh', 'hh_access_control_expose_headers_custom');
708 register_setting('http-headers-acma', 'hh_access_control_max_age');
709 register_setting('http-headers-acma', 'hh_access_control_max_age_value');
710 register_setting('http-headers-ce', 'hh_content_encoding');
711 register_setting('http-headers-ce', 'hh_content_encoding_module');
712 register_setting('http-headers-ce', 'hh_content_encoding_value');
713 register_setting('http-headers-ce', 'hh_content_encoding_ext');
714 register_setting('http-headers-vary', 'hh_vary');
715 register_setting('http-headers-vary', 'hh_vary_value');
716 register_setting('http-headers-xpb', 'hh_x_powered_by');
717 register_setting('http-headers-xpb', 'hh_x_powered_by_option');
718 register_setting('http-headers-xpb', 'hh_x_powered_by_value');
719 register_setting('http-headers-wwa', 'hh_www_authenticate');
720 register_setting('http-headers-wwa', 'hh_www_authenticate_type');
721 register_setting('http-headers-wwa', 'hh_www_authenticate_realm');
722 register_setting('http-headers-wwa', 'hh_www_authenticate_user');
723 register_setting('http-headers-wwa', 'hh_www_authenticate_pswd');
724 register_setting('http-headers-cc', 'hh_cache_control');
725 register_setting('http-headers-cc', 'hh_cache_control_value');
726 register_setting('http-headers-age', 'hh_age');
727 register_setting('http-headers-age', 'hh_age_value');
728 register_setting('http-headers-pra', 'hh_pragma');
729 register_setting('http-headers-pra', 'hh_pragma_value');
730 register_setting('http-headers-exp', 'hh_expires');
731 register_setting('http-headers-exp', 'hh_expires_value');
732 register_setting('http-headers-exp', 'hh_expires_type');
733 register_setting('http-headers-con', 'hh_connection');
734 register_setting('http-headers-con', 'hh_connection_value');
735 register_setting('http-headers-cose', 'hh_cookie_security');
736 register_setting('http-headers-cose', 'hh_cookie_security_value');
737 register_setting('http-headers-ect', 'hh_expect_ct');
738 register_setting('http-headers-ect', 'hh_expect_ct_max_age');
739 register_setting('http-headers-ect', 'hh_expect_ct_report_uri');
740 register_setting('http-headers-ect', 'hh_expect_ct_enforce');
741 register_setting('http-headers-tao', 'hh_timing_allow_origin');
742 register_setting('http-headers-tao', 'hh_timing_allow_origin_value');
743 register_setting('http-headers-tao', 'hh_timing_allow_origin_url');
744 register_setting('http-headers-che', 'hh_custom_headers');
745 register_setting('http-headers-che', 'hh_custom_headers_value');
746 register_setting('http-headers-xdo', 'hh_x_download_options');
747 register_setting('http-headers-xdo', 'hh_x_download_options_value');
748 register_setting('http-headers-xpcd', 'hh_x_permitted_cross_domain_policies');
749 register_setting('http-headers-xpcd', 'hh_x_permitted_cross_domain_policies_value');
750 register_setting('http-headers-xdpc', 'hh_x_dns_prefetch_control');
751 register_setting('http-headers-xdpc', 'hh_x_dns_prefetch_control_value');
752 register_setting('http-headers-rt', 'hh_report_to');
753 register_setting('http-headers-rt', 'hh_report_to_value');
754 register_setting('http-headers-fp', 'hh_feature_policy');
755 register_setting('http-headers-fp', 'hh_feature_policy_value');
756 register_setting('http-headers-fp', 'hh_feature_policy_feature');
757 register_setting('http-headers-fp', 'hh_feature_policy_origin');
758 register_setting('http-headers-pp', 'hh_permissions_policy');
759 register_setting('http-headers-pp', 'hh_permissions_policy_value');
760 register_setting('http-headers-pp', 'hh_permissions_policy_feature');
761 register_setting('http-headers-pp', 'hh_permissions_policy_origin');
762 register_setting('http-headers-csd', 'hh_clear_site_data');
763 register_setting('http-headers-csd', 'hh_clear_site_data_value');
764 register_setting('http-headers-cty', 'hh_content_type');
765 register_setting('http-headers-cty', 'hh_content_type_value');
766 register_setting('http-headers-corp', 'hh_cross_origin_resource_policy');
767 register_setting('http-headers-corp', 'hh_cross_origin_resource_policy_value');
768 register_setting('http-headers-nel', 'hh_nel');
769 register_setting('http-headers-nel', 'hh_nel_value');
770 register_setting('http-headers-coep', 'hh_cross_origin_embedder_policy');
771 register_setting('http-headers-coep', 'hh_cross_origin_embedder_policy_value');
772 register_setting('http-headers-coop', 'hh_cross_origin_opener_policy');
773 register_setting('http-headers-coop', 'hh_cross_origin_opener_policy_value');
774 register_setting('http-headers-rob', 'hh_x_robots_tag');
775 register_setting('http-headers-rob', 'hh_x_robots_tag_value');
776 }
777
778 function http_headers_option($option) {
779
780 include_once ABSPATH . 'wp-admin/includes/admin.php';
781
782 require_once ABSPATH . WPINC . '/pluggable.php';
783
784 if (isset($_POST['hh_method']))
785 {
786 check_admin_referer('http-headers-mtd-options');
787 # When method is changed
788 http_headers_activate();
789
790 } elseif (is_apache_mode()) {
791 # When particular header is changed
792 switch (true) {
793 case array_key_exists('hh_www_authenticate', $_POST):
794 check_admin_referer('http-headers-wwa-options');
795 update_auth_credentials();
796 update_auth_directives();
797 break;
798 case array_key_exists('hh_content_encoding', $_POST):
799 check_admin_referer('http-headers-ce-options');
800 update_content_encoding_directives();
801 break;
802 case array_key_exists('hh_content_type', $_POST):
803 check_admin_referer('http-headers-cty-options');
804 update_content_type_directives();
805 break;
806 case array_key_exists('hh_expires', $_POST):
807 check_admin_referer('http-headers-exp-options');
808 update_expires_directives();
809 break;
810 case array_key_exists('hh_cookie_security', $_POST):
811 check_admin_referer('http-headers-cose-options');
812 update_cookie_security_directives();
813 break;
814 case array_key_exists('hh_timing_allow_origin', $_POST):
815 check_admin_referer('http-headers-tao-options');
816 update_timing_directives();
817 break;
818 case array_key_exists('option_page', $_POST) && strpos($_POST['option_page'], 'http-headers-') === 0:
819 check_admin_referer($_POST['option_page'].'-options');
820 update_headers_directives();
821 break;
822 }
823 }
824 }
825
826 function nginx_headers_directives() {
827 $lines = array();
828 list($headers, , $unset, $append) = get_http_headers();
829
830 foreach ($unset as $header) {
831 $lines[] = sprintf(' more_clear_headers "%s";', $header);
832 }
833 $cors = $cors_header = $cors_inner = $cors_footer = array();
834 $all = array();
835 foreach ($headers as $key => $value) {
836 if (in_array($key, array('WWW-Authenticate'))) {
837 continue;
838 }
839 if (in_array($key, array('X-Content-Type-Options'))) {
840 $all[] = sprintf('add_header %s %s always;', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
841 continue;
842 }
843 if ($key == 'Access-Control-Allow-Origin' && is_array($value)) {
844 $cors_header[] = sprintf('if ($http_origin ~* ^(%s)$) {', str_replace('.', '\.', join('|', $value)));
845 $cors_footer[] = '}';
846 $cors_inner[] = ' add_header Access-Control-Allow-Origin "$http_origin";';
847 if (!in_array('*', $value))
848 {
849 $cors_inner[] = ' add_header Vary "Origin";';
850 }
851 continue;
852 }
853 if (in_array($key, array('Access-Control-Allow-Headers', 'Access-Control-Allow-Methods', 'Access-Control-Allow-Credentials', 'Access-Control-Max-Age', 'Access-Control-Expose-Headers'))) {
854 $cors_inner[] = sprintf(' add_header %s %s;', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
855 continue;
856 }
857 $lines[] = sprintf(' add_header %s %s;', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
858 }
859 foreach ($append as $key => $value) {
860 $lines[] = sprintf(' add_header %s %s;', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
861 }
862 if (!empty($cors_inner))
863 {
864 $cors = array_merge(
865 $cors_header,
866 $cors_inner,
867 $cors_footer
868 );
869 }
870 if (!empty($lines)) {
871 $lines = array_merge(
872 $all,
873 $cors,
874 array('location ~* \.(php|html)$ {'),
875 $lines,
876 array('}')
877 );
878 }
879 return $lines;
880 }
881
882 function nginx_content_encoding_directives() {
883 $lines = array();
884 if (get_option('hh_content_encoding') == 1) {
885
886 $lines[] = 'gzip on;';
887
888 $content_encoding_value = get_option('hh_content_encoding_value');
889 if (!$content_encoding_value) {
890 $content_encoding_value = array();
891 }
892
893 $content_encoding_ext = get_option('hh_content_encoding_ext');
894 if (!$content_encoding_ext) {
895 $content_encoding_ext = array();
896 }
897 if (!empty($content_encoding_ext)) {
898 //$lines[] = sprintf('<FilesMatch "\.(%s)$">', join('|', array_keys($content_encoding_ext)));
899 }
900 if (!empty($content_encoding_value)) {
901 $lines[] = sprintf('gzip_types %s;', join(' ', array_keys($content_encoding_value)));
902 }
903 }
904 return $lines;
905 }
906
907 function nginx_content_type_directives() {
908 $lines = array();
909 if (get_option('hh_content_type') == 1) {
910 $values = get_option('hh_content_type_value', array());
911 foreach ($values as $ext => $media_type) {
912 $lines[] = sprintf("%s %s;", $media_type, $ext);
913 }
914 }
915
916 return $lines;
917 }
918
919 function nginx_expires_directives() {
920 $lines = array();
921 if (get_option('hh_expires') == 1) {
922
923 $types = get_option('hh_expires_type', array());
924 $values = get_option('hh_expires_value', array());
925
926 $lines[] = 'map $sent_http_content_type $expires {';
927 foreach (array_keys($types) as $type) {
928 list($base, $period, $suffix) = explode('_', $values[$type]);
929 if (in_array($base, array('access', 'modification'))) {
930 $lines[] = $type != 'default'
931 ? sprintf(' %s %u%s;', $type, $period, $suffix[0])
932 : sprintf(' default %u%s;', $period, $suffix[0]);
933 } elseif ($base == 'invalid') {
934 $lines[] = $type != 'default'
935 ? sprintf(' %s 0;', $type)
936 : sprintf(' default 0;');
937 }
938 }
939 $lines[] = '}';
940
941 $lines[] = 'expires $expires;';
942 }
943 return $lines;
944 }
945
946 function nginx_timing_directives() {
947 $lines = array();
948 if (get_option('hh_timing_allow_origin') == 1) {
949 $value = get_option('hh_timing_allow_origin_value');
950 switch ($value)
951 {
952 case 'origin':
953 $value = get_option('hh_timing_allow_origin_url');
954 break;
955 }
956 if (!empty($value))
957 {
958 $lines[] = 'location ~* \.(js|css|jpe?g|png|gif|eot|otf|svg|ttf|woff2?)$ {';
959 $lines[] = sprintf(' add_header Timing-Allow-Origin "%s";', $value);
960 $lines[] = '}';
961 }
962 }
963 return $lines;
964 }
965
966 function nginx_auth_directives() {
967 $lines = array();
968 if (get_option('hh_www_authenticate') == 1) {
969
970 $type = get_option('hh_www_authenticate_type');
971
972 $file = $type == 'Basic' ? get_htpasswd_filename() : get_htdigest_filename();
973
974 $lines[] = sprintf('location ~ ^%s$ {', str_replace('.', '\.', basename($file)));
975 $lines[] = ' deny all;';
976 $lines[] = '}';
977
978 $lines[] = sprintf('location %s {', get_home_path());
979 if ($type == 'Basic') {
980 $lines[] = sprintf(' auth_basic "%s";', get_option('hh_www_authenticate_realm'));
981 $lines[] = sprintf(' auth_basic_user_file %s;', $file);
982 } else {
983 $lines[] = sprintf(' auth_digest "%s";', get_option('hh_www_authenticate_realm'));
984 $lines[] = sprintf(' auth_digest_user_file %s;', $file);
985 }
986 $lines[] = '}';
987 }
988 return $lines;
989 }
990
991 function nginx_auth_credentials() {
992 return apache_auth_credentials();
993 }
994
995 function nginx_cookie_security_directives() {
996 $lines = array();
997
998 //TODO
999
1000 return $lines;
1001 }
1002
1003 function nginx_check_requirements() {
1004 //TODO scheduled for v2.0.0
1005 return true;
1006 }
1007
1008 function iis_headers_directives() {
1009 //TODO scheduled for v2.0.0
1010 }
1011
1012 function iis_content_encoding_directives() {
1013 //TODO scheduled for v2.0.0
1014 }
1015
1016 function iis_content_type_directives() {
1017 //TODO scheduled for v2.0.0
1018 }
1019
1020 function iis_expires_directives() {
1021 //TODO scheduled for v2.0.0
1022 }
1023
1024 function iis_timing_directives() {
1025 //TODO scheduled for v2.0.0
1026 }
1027
1028 function iis_auth_directives() {
1029 //TODO scheduled for v2.0.0
1030 }
1031
1032 function iis_auth_credentials() {
1033 //TODO scheduled for v2.0.0
1034 }
1035
1036 function iis_cookie_security_directives() {
1037 //TODO scheduled for v2.0.0
1038 }
1039
1040 function iis_check_requirements() {
1041 //TODO scheduled for v2.0.0
1042 return true;
1043 }
1044
1045 function apache_headers_directives() {
1046 $lines = array();
1047 list($headers, , $unset, $append) = get_http_headers();
1048
1049 foreach ($unset as $header) {
1050 $lines[] = sprintf(' Header always unset %s', $header);
1051 $lines[] = sprintf(' Header unset %s', $header);
1052 }
1053 $all = array();
1054 foreach ($headers as $key => $value) {
1055 if (in_array($key, array('WWW-Authenticate'))) {
1056 continue;
1057 }
1058 if (in_array($key, array('X-Content-Type-Options'))) {
1059 $all[] = sprintf(' Header always set %s %s', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
1060 continue;
1061 }
1062 if ($key == 'Strict-Transport-Security') {
1063 $lines[] = sprintf(' Header set %s %s env=HTTPS', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
1064 continue;
1065 }
1066 if ($key == 'Access-Control-Allow-Origin') {
1067 $all[] = ' <IfModule mod_setenvif.c>';
1068 if (!is_array($value)) {
1069 if ($value) {
1070 $value = array($value);
1071 } else {
1072 $value = array();
1073 }
1074 }
1075 //$value[] = 'null';
1076 if (is_array($value))
1077 {
1078 $all[] = sprintf(' SetEnvIf Origin "^(%s)$" CORS=$0', str_replace(array('.', '*'), array('\.', '.+'), join('|', $value)));
1079 } else {
1080 $all[] = ' SetEnvIf Origin "^(.+)$" CORS=$0';
1081 }
1082 $all[] = ' </IfModule>';
1083 $all[] = ' Header set Access-Control-Allow-Origin %{CORS}e env=CORS';
1084 if (!in_array('*', $value))
1085 {
1086 $all[] = ' Header append Vary "Origin" env=CORS';
1087 }
1088 continue;
1089 }
1090 if (in_array($key, array('Access-Control-Allow-Headers', 'Access-Control-Allow-Methods', 'Access-Control-Allow-Credentials', 'Access-Control-Max-Age', 'Access-Control-Expose-Headers'))) {
1091 $all[] = sprintf(' Header set %s %s env=CORS', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
1092 continue;
1093 }
1094 $lines[] = sprintf(' Header set %s %s', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
1095 }
1096 foreach ($append as $key => $value) {
1097 $lines[] = sprintf(' Header append %s %s', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
1098 }
1099 if (!empty($lines) || !empty($all)) {
1100 $lines = array_merge(
1101 array('<IfModule mod_headers.c>'),
1102 $all,
1103 array(' <FilesMatch "\.(php|html)$">'),
1104 $lines,
1105 array(' </FilesMatch>', '</IfModule>')
1106 );
1107 }
1108 return $lines;
1109 }
1110
1111 function apache_content_encoding_directives() {
1112 $lines = array();
1113 if (get_option('hh_content_encoding') == 1) {
1114
1115 $content_encoding_module = get_option('hh_content_encoding_module');
1116
1117 $module = 'mod_deflate.c';
1118 $filter = 'DEFLATE';
1119 $accept_encoding = 'gzip';
1120
1121 if ($content_encoding_module == 'brotli') {
1122 $module = 'mod_brotli.c';
1123 $filter = 'BROTLI_COMPRESS';
1124 $accept_encoding = 'br';
1125 }
1126
1127 $content_encoding_value = get_option('hh_content_encoding_value');
1128 if (!$content_encoding_value) {
1129 $content_encoding_value = array();
1130 }
1131
1132 $content_encoding_ext = get_option('hh_content_encoding_ext');
1133 if (!$content_encoding_ext) {
1134 $content_encoding_ext = array();
1135 }
1136
1137 $type = join('|', array_keys($content_encoding_value));
1138 $ext = join('|', array_keys($content_encoding_ext));
1139
1140 if (!empty($type) && !empty($ext)) {
1141 $expression = sprintf('(%%{CONTENT_TYPE} =~ m#^(%1$s)# || %%{REQUEST_FILENAME} =~ /.(%2$s)$/)', $type, $ext);
1142 } elseif (!empty($type)) {
1143 $expression = sprintf('%%{CONTENT_TYPE} =~ m#^(%1$s)#', $type);
1144 } elseif (!empty($ext)) {
1145 $expression = sprintf('%%{REQUEST_FILENAME} =~ /.(%1$s)$/', $ext);
1146 }
1147
1148 if (isset($expression)) {
1149 $lines[] = '<IfModule mod_filter.c>';
1150 $lines[] = ' FilterDeclare HttpHeaders';
1151 if (in_array($content_encoding_module, array('brotli', 'deflate'))) {
1152 $lines[] = sprintf('<IfModule %s>', $module);
1153 $lines[] = sprintf(' FilterProvider HttpHeaders %1$s "%%{HTTP:Accept-Encoding} =~ /%2$s/ && %3$s"', $filter, $accept_encoding, $expression);
1154 $lines[] = ' </IfModule>';
1155 } else {
1156 $lines[] = ' <IfModule mod_deflate.c>';
1157 $lines[] = ' <IfModule !mod_brotli.c>';
1158 $lines[] = sprintf(' FilterProvider HttpHeaders DEFLATE "%%{HTTP:Accept-Encoding} =~ /gzip/ && %1$s"', $expression);
1159 $lines[] = ' </IfModule>';
1160 $lines[] = ' </IfModule>';
1161 $lines[] = ' <IfModule mod_brotli.c>';
1162 $lines[] = sprintf(' FilterProvider HttpHeaders BROTLI_COMPRESS "%%{HTTP:Accept-Encoding} =~ /br/ && %1$s"', $expression);
1163 $lines[] = ' </IfModule>';
1164 }
1165 $lines[] = ' FilterChain HttpHeaders';
1166 $lines[] = '</IfModule>';
1167 }
1168 }
1169
1170 return $lines;
1171 }
1172
1173 function apache_expires_directives() {
1174 $lines = array();
1175 if (get_option('hh_expires') == 1) {
1176
1177 $types = get_option('hh_expires_type', array());
1178 $values = get_option('hh_expires_value', array());
1179 if (!is_array($types))
1180 {
1181 $types = array();
1182 }
1183 if (!is_array($values))
1184 {
1185 $values = array();
1186 }
1187
1188 $lines[] = '<IfModule mod_expires.c>';
1189 $lines[] = ' ExpiresActive On';
1190 foreach (array_keys($types) as $type) {
1191 list($base, $period, $suffix) = explode('_', $values[$type]);
1192 if (in_array($base, array('access', 'modification'))) {
1193 $lines[] = $type != 'default'
1194 ? sprintf(' ExpiresByType %s "%s plus %u %s"', $type, $base, $period, $suffix)
1195 : sprintf(' ExpiresDefault "%s plus %u %s"', $base, $period, $suffix);
1196 } elseif ($base == 'invalid') {
1197 $lines[] = $type != 'default'
1198 ? sprintf(' ExpiresByType %s A0', $type)
1199 : sprintf(' ExpiresDefault A0');
1200 }
1201 }
1202 $lines[] = '</IfModule>';
1203 }
1204
1205 return $lines;
1206 }
1207
1208 function apache_content_type_directives() {
1209 $lines = array();
1210 if (get_option('hh_content_type') == 1) {
1211 $values = get_option('hh_content_type_value', array());
1212 $lines[] = '<IfModule mod_mime.c>';
1213 foreach ($values as $ext => $media_type) {
1214 $lines[] = sprintf(" AddType %s .%s", $media_type, $ext);
1215 }
1216 $lines[] = '</IfModule>';
1217 }
1218
1219 return $lines;
1220 }
1221
1222 function apache_timing_directives() {
1223 $lines = array();
1224 if (get_option('hh_timing_allow_origin') == 1) {
1225 $value = get_option('hh_timing_allow_origin_value');
1226 switch ($value)
1227 {
1228 case 'origin':
1229 $value = get_option('hh_timing_allow_origin_url');
1230 break;
1231 }
1232 if (!empty($value))
1233 {
1234 $lines[] = '<IfModule mod_headers.c>';
1235 $lines[] = ' <FilesMatch "\\.(js|css|jpe?g|png|gif|eot|otf|svg|ttf|woff2?)$">';
1236 $lines[] = sprintf(' Header set Timing-Allow-Origin "%s"', $value);
1237 $lines[] = ' </FilesMatch>';
1238 $lines[] = '</IfModule>';
1239 }
1240 }
1241
1242 return $lines;
1243 }
1244
1245 function apache_auth_directives() {
1246 $lines = array();
1247 if (get_option('hh_www_authenticate') == 1) {
1248
1249 $type = get_option('hh_www_authenticate_type');
1250
1251 $file = $type == 'Basic' ? get_htpasswd_filename() : get_htdigest_filename();
1252
1253 $lines[] = sprintf('<FilesMatch "^%s$">', str_replace('.', '\.', basename($file)));
1254 $lines[] = ' <IfModule mod_authz_core.c>';
1255 $lines[] = ' Require all denied';
1256 $lines[] = ' </IfModule>';
1257 $lines[] = ' <IfModule !mod_authz_core.c>';
1258 $lines[] = ' Order deny,allow';
1259 $lines[] = ' Deny from all';
1260 $lines[] = ' </IfModule>';
1261 $lines[] = '</FilesMatch>';
1262 // no empty AuthName
1263 $realm = get_option('hh_www_authenticate_realm'); // AuthName
1264 $realm = ($realm == '') ? 'restricted area':$realm; // Empty => give fixed value
1265
1266 $lines[] = sprintf('<IfModule mod_auth_%s.c>', strtolower($type));
1267 $lines[] = sprintf(' AuthType %s', get_option('hh_www_authenticate_type'));
1268 $lines[] = sprintf(' AuthName "%s"', $realm);
1269 $lines[] = sprintf(' AuthUserFile "%s"', $file);
1270 $lines[] = ' Require valid-user';
1271 $lines[] = '</IfModule>';
1272 }
1273
1274 return $lines;
1275 }
1276
1277 function apache_auth_credentials() {
1278 if (get_option('hh_www_authenticate') == 1) {
1279 $type = get_option('hh_www_authenticate_type');
1280 $usernames = get_option('hh_www_authenticate_user', array());
1281 $passwords = get_option('hh_www_authenticate_pswd', array());
1282 if (!is_array($usernames)) {
1283 $usernames = array($usernames);
1284 }
1285 if (!is_array($passwords)) {
1286 $passwords = array($passwords);
1287 }
1288 $realm = get_option('hh_www_authenticate_realm');
1289 $auth = array();
1290 switch ($type) {
1291 case 'Basic':
1292 $ht_file = get_htpasswd_filename();
1293 foreach ($usernames as $k => $user) {
1294 $auth[] = sprintf('%s:{SHA}%s', $user, base64_encode(sha1($passwords[$k], true)));
1295 }
1296 break;
1297 case 'Digest':
1298 $ht_file = get_htdigest_filename();
1299 foreach ($usernames as $k => $user) {
1300 $auth[] = sprintf('%s:%s:%s', $user, $realm, md5($user.':'.$realm.':'.$passwords[$k]));
1301 }
1302 break;
1303 }
1304 $auth = join("\n", $auth);
1305
1306 return compact('ht_file', 'auth');
1307 }
1308 return false;
1309 }
1310
1311 function apache_cookie_security_directives() {
1312 $lines = array();
1313 if (get_option('hh_cookie_security') == 1) {
1314 $value = get_option('hh_cookie_security_value', array());
1315 $str = '';
1316 if (isset($value['HttpOnly'])) {
1317 $str .= ';HttpOnly';
1318 }
1319 if (isset($value['Secure'])) {
1320 $str .= ';Secure';
1321 }
1322 if (isset($value['SameSite']) && in_array($value['SameSite'], array('None', 'Lax', 'Strict'))) {
1323 $str .= ';SameSite=' . $value['SameSite'];
1324 }
1325 if ($str) {
1326 $lines[] = '<IfModule mod_headers.c>';
1327 $lines[] = ' Header always edit Set-Cookie (.*) "$1'.$str.'"';
1328 $lines[] = '</IfModule>';
1329 }
1330 }
1331
1332 return $lines;
1333 }
1334
1335 function apache_check_requirements() {
1336 return check_filename(get_htaccess_filename());
1337 }
1338
1339 function update_headers_directives() {
1340 $result = false;
1341 if (is_apache_mode()) {
1342 $lines = apache_headers_directives();
1343 $result = insert_with_markers(get_htaccess_filename(), "HttpHeaders", $lines);
1344 }
1345
1346 return $result;
1347 }
1348
1349 function update_content_encoding_directives() {
1350 $lines = array();
1351 if (is_apache_mode()) {
1352 $lines = apache_content_encoding_directives();
1353 }
1354
1355 return insert_with_markers(get_htaccess_filename(), "HttpHeadersCompression", $lines);
1356 }
1357
1358 function update_expires_directives() {
1359 $lines = array();
1360 if (is_apache_mode()) {
1361 $lines = apache_expires_directives();
1362 }
1363
1364 return insert_with_markers(get_htaccess_filename(), "HttpHeadersExpires", $lines);
1365 }
1366
1367 function update_content_type_directives() {
1368 $lines = array();
1369 if (is_apache_mode()) {
1370 $lines = apache_content_type_directives();
1371 }
1372
1373 return insert_with_markers(get_htaccess_filename(), "HttpHeadersContentType", $lines);
1374 }
1375
1376 function update_timing_directives() {
1377 $lines = array();
1378 if (is_apache_mode()) {
1379 $lines = apache_timing_directives();
1380 }
1381
1382 return insert_with_markers(get_htaccess_filename(), "HttpHeadersTiming", $lines);
1383 }
1384
1385 function update_auth_directives() {
1386 $lines = array();
1387 if (is_apache_mode()) {
1388 $lines = apache_auth_directives();
1389 }
1390
1391 return insert_with_markers(get_htaccess_filename(), "HttpHeadersAuth", $lines);
1392 }
1393
1394 function update_auth_credentials() {
1395 if (is_apache_mode()) {
1396 $credentials = apache_auth_credentials();
1397 if (isset($credentials['ht_file']) && !empty($credentials['ht_file']))
1398 {
1399 return @file_put_contents($credentials['ht_file'], $credentials['auth'], LOCK_EX);
1400 }
1401 }
1402
1403 return false;
1404 }
1405
1406 function update_cookie_security_directives() {
1407 $lines = array();
1408 $is_apache = is_apache_mode();
1409 $htaccess = get_htaccess_filename();
1410 $is_cgi = strpos(PHP_SAPI, 'cgi') !== false;
1411 if ($is_cgi) {
1412 $filename = get_user_ini_filename();
1413 $lines = php_cookie_security_directives();
1414 } elseif ($is_apache) {
1415 $filename = $htaccess;
1416 $lines = apache_cookie_security_directives();
1417 }
1418
1419 if (!$is_apache) {
1420 insert_with_markers($htaccess, "HttpHeadersCookieSecurity", array());
1421 }
1422
1423 if ($is_cgi) {
1424 return update_user_ini_filename($filename, "HttpHeadersCookieSecurity", $lines);
1425 }
1426
1427 return insert_with_markers($filename, "HttpHeadersCookieSecurity", $lines);
1428 }
1429
1430 function update_user_ini_filename($filename, $marker, $insertion) {
1431 if (!is_array($insertion)) {
1432 $insertion = explode("\n", $insertion);
1433 }
1434
1435 $start_marker = "; BEGIN " . $marker;
1436 $end_marker = "; END " . $marker;
1437
1438 $data = "";
1439 if (is_file($filename)) {
1440 $data = @file_get_contents($filename);
1441 }
1442
1443 $string = $start_marker;
1444 if ($insertion)
1445 {
1446 $string .= "\n".join("\n", $insertion);
1447 }
1448 $string .= "\n".$end_marker;
1449
1450 $pattern = '/'.$start_marker.'.*'.$end_marker.'/isU';
1451
1452 if (preg_match($pattern, $data)) {
1453 $data = preg_replace($pattern, $string, $data);
1454 } else {
1455 $data .= "\n".$string;
1456 }
1457
1458 $bytes = @file_put_contents($filename, $data, LOCK_EX);
1459
1460 return !!$bytes;
1461 }
1462
1463 function is_php_mode() {
1464 return get_option('hh_method') == 'php';
1465 }
1466
1467 function is_apache_mode() {
1468 return get_option('hh_method') == 'htaccess';
1469 }
1470
1471 function is_samesite_supported() {
1472 return version_compare(PHP_VERSION, '7.3.0', '>=');
1473 }
1474
1475 function http_headers_text_domain() {
1476 load_plugin_textdomain('http-headers', false, basename( dirname( __FILE__ ) ) . '/languages/');
1477 }
1478
1479 function http_headers_settings_link( $links ) {
1480 $url = get_admin_url() . 'options-general.php?page=http-headers';
1481 $settings_link = '<a href="' . $url . '">' . __('Settings', 'http-headers') . '</a>';
1482 array_unshift( $links, $settings_link );
1483 return $links;
1484 }
1485
1486 function http_headers_after_setup_theme() {
1487 add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'http_headers_settings_link');
1488 }
1489
1490 function http_headers_enqueue($hook) {
1491 if ( 'http-headers.php' != $hook ) {
1492 # FIXME
1493 //return;
1494 }
1495
1496 wp_enqueue_script('http_headers_admin_scripts', plugin_dir_url( __FILE__ ) . 'assets/scripts.js', array(), '1.16.1', true);
1497 wp_localize_script('http_headers_admin_scripts', 'hh', array(
1498 'lbl_delete' => __('Delete', 'http-headers'),
1499 'lbl_value' => __('Value', 'http-headers'),
1500 'lbl_remove_endpoint' => __('Remove endpoint', 'http-headers'),
1501 'lbl_remove_group' => __('Remove group', 'http-headers'),
1502 ));
1503 wp_enqueue_style('http_headers_admin_styles', plugin_dir_url( __FILE__ ) . 'assets/styles.css', array(), '1.16.1');
1504 }
1505
1506 function http_headers_ajax_inspect() {
1507 check_ajax_referer('inspect');
1508 if (current_user_can('manage_options')) {
1509 include 'views/ajax-inspect.php';
1510 }
1511 wp_die();
1512 }
1513
1514 function http_headers_post_import() {
1515 check_admin_referer('import');
1516 if (!is_super_admin()) {
1517 wp_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=ERR&code=102", get_admin_url()));
1518 exit;
1519 }
1520 global $wpdb;
1521 if (!(isset($_FILES['file']['tmp_name'])
1522 && is_uploaded_file($_FILES['file']['tmp_name'])
1523 && $_FILES['file']['error'] == UPLOAD_ERR_OK
1524 )) {
1525 wp_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=ERR&code=100", get_admin_url()));
1526 exit;
1527 }
1528
1529 $string = @file_get_contents($_FILES['file']['tmp_name']);
1530 if ($string === false) {
1531 wp_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=ERR&code=101", get_admin_url()));
1532 exit;
1533 }
1534
1535 $arr = preg_split('/;(\s+)?\n/', $string);
1536 foreach ($arr as $statement) {
1537 $statement = preg_replace("/(INSERT\s*INTO\s*)[\w\_]+options/", '${1}'.$wpdb->options, $statement);
1538 $wpdb->query($statement);
1539 }
1540
1541 wp_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=OK", get_admin_url()));
1542 exit;
1543 }
1544
1545 function http_headers_post_export() {
1546 check_admin_referer('export');
1547 if (!is_super_admin()) {
1548 wp_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=ERR&code=102", get_admin_url()));
1549 exit;
1550 }
1551 global $wpdb;
1552 $options = include dirname(__FILE__) . '/views/includes/options.inc.php';
1553 $opts = array();
1554 foreach ($options as $option)
1555 {
1556 $opts[] = $option[0];
1557 }
1558 $statement = sprintf("SELECT * FROM %s WHERE option_name IN ('%s');", $wpdb->options, join("','", $opts));
1559 $results = $wpdb->get_results($statement, ARRAY_A);
1560 $sql = array();
1561
1562 $indexes = array();
1563 foreach ($options as $option)
1564 {
1565 foreach ($results as $item)
1566 {
1567 if ($item['option_name'] == $option[0])
1568 {
1569 $indexes[$option[0]] = 1;
1570
1571 $value = str_replace("'", "''", $item['option_value']);
1572 $query = array();
1573 $query[] = sprintf("INSERT INTO %s (option_id, option_name, option_value, autoload)", $wpdb->options);
1574 $query[] = sprintf("VALUES (NULL, '%s', '%s', '%s')", $item['option_name'], $value, $item['autoload']);
1575 $query[] = sprintf("ON DUPLICATE KEY UPDATE option_value = '%s', autoload = '%s';", $value, $item['autoload']);
1576 $sql[] = join("\n", $query);
1577 break;
1578 }
1579 }
1580
1581 if (!isset($indexes[$option[0]]))
1582 {
1583 $query = array();
1584 $query[] = sprintf("INSERT INTO %s (option_id, option_name, option_value, autoload)", $wpdb->options);
1585 $query[] = sprintf("VALUES (NULL, '%s', '%s', 'yes')", $option[0], $option[1]);
1586 $query[] = sprintf("ON DUPLICATE KEY UPDATE option_value = '%s', autoload = 'yes';", $option[1]);
1587 $sql[] = join("\n", $query);
1588 }
1589 }
1590
1591 $sql = join("\n\n", $sql);
1592 $length = function_exists('mb_strlen') ? mb_strlen($sql) : strlen($sql);
1593 $name = sprintf('WP-HTTP-Headers-%u.sql', time());
1594
1595 # Send headers
1596 header('Pragma: public');
1597 header('Expires: 0');
1598 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
1599 header('Cache-Control: private', false);
1600 header('Content-Transfer-Encoding: binary');
1601 header('Content-Disposition: attachment; filename="'.$name.'";');
1602 header('Content-Type: application/sql');
1603 header('Content-Length: ' . $length);
1604
1605 echo $sql;
1606 exit;
1607 }
1608
1609 function check_filename($filename) {
1610 if (!is_file($filename)) {
1611 return -1;
1612 }
1613
1614 clearstatcache();
1615 if (!is_writable($filename)) {
1616 return -2;
1617 }
1618
1619 return true;
1620 }
1621
1622 function get_web_server_filename() {
1623 if (is_apache_mode()) {
1624 return get_htaccess_filename();
1625 }
1626
1627 return NULL;
1628 }
1629
1630 function check_web_server_requirements() {
1631 if (is_apache_mode()) {
1632 return apache_check_requirements();
1633 }
1634
1635 return true;
1636 }
1637
1638 function check_php_requirements() {
1639 if (strpos(PHP_SAPI, 'cgi') !== false) {
1640 // cgi, cgi-fcgi, fpm-fcgi
1641 return check_filename(get_user_ini_filename());
1642 }
1643
1644 return true;
1645 }
1646
1647 function http_headers_logout() {
1648 if (get_option('hh_clear_site_data') == 1) {
1649 $values = get_option('hh_clear_site_data_value', array());
1650 $tmp = array_keys($values);
1651 if ($tmp) {
1652 header(sprintf('Clear-Site-Data: "%s"', join('", "', $tmp)));
1653 }
1654 }
1655 }
1656
1657 function http_headers_activate() {
1658 update_headers_directives();
1659 update_auth_credentials();
1660 update_auth_directives();
1661 update_content_encoding_directives();
1662 update_content_type_directives();
1663 update_expires_directives();
1664 update_cookie_security_directives();
1665 update_timing_directives();
1666 }
1667
1668 function http_headers_deactivate() {
1669 $filename = get_htaccess_filename();
1670
1671 insert_with_markers($filename, "HttpHeaders", array());
1672 insert_with_markers($filename, "HttpHeadersCompression", array());
1673 insert_with_markers($filename, "HttpHeadersContentType", array());
1674 insert_with_markers($filename, "HttpHeadersExpires", array());
1675 insert_with_markers($filename, "HttpHeadersTiming", array());
1676 insert_with_markers($filename, "HttpHeadersAuth", array());
1677 insert_with_markers($filename, "HttpHeadersCookieSecurity", array());
1678 }
1679
1680 register_activation_hook(__FILE__, 'http_headers_activate');
1681 register_deactivation_hook(__FILE__, 'http_headers_deactivate');
1682 add_action('wp_logout', 'http_headers_logout');
1683
1684 if ( is_admin() ){ // admin actions
1685 add_action('admin_menu', 'http_headers_admin_add_page');
1686 add_action('admin_init', 'http_headers_admin');
1687 add_action("added_option", 'http_headers_option');
1688 add_action("updated_option", 'http_headers_option');
1689 add_action('admin_enqueue_scripts', 'http_headers_enqueue');
1690 add_action('after_setup_theme', 'http_headers_after_setup_theme');
1691 add_action('plugins_loaded', 'http_headers_text_domain');
1692 add_action('wp_ajax_inspect', 'http_headers_ajax_inspect');
1693 add_action('admin_post_import', 'http_headers_post_import');
1694 add_action('admin_post_export', 'http_headers_post_export');
1695 } else {
1696 // non-admin enqueues, actions, and filters
1697 add_action('send_headers', 'http_headers');
1698 }
1699
1700 function http_headers_admin_page() {
1701 include 'views/index.php';
1702 }