PluginProbe
HTTP Headers / 1.18.4
HTTP Headers v1.18.4
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.4, at http-headers.php

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