PluginProbe
HTTP Headers / 1.18.2
HTTP Headers v1.18.2
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.2, at http-headers.php

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