diff --git a/.gitignore b/.gitignore index 2b4bcaa..19b6fc5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ vendor settings.php nbproject/private *.sync-conflict* -database.mwb.bak \ No newline at end of file +database.mwb.bak diff --git a/README.md b/README.md index f7439da..9d2d97d 100644 --- a/README.md +++ b/README.md @@ -36,4 +36,4 @@ Installing 4. Set the location of the AccountHub API in `settings.php` (see "PORTAL_API") and enter an API key ("PORTAL_KEY") 5. Set the location of the AccountHub home page ("PORTAL_URL") 6. Set the URL of this app ("URL") -7. Run `composer install` (or `composer.phar install`) to install dependency libraries. \ No newline at end of file +7. Run `composer install` (or `composer.phar install`) to install dependency libraries. diff --git a/action.php b/action.php index 80a6c09..e98bf5d 100644 --- a/action.php +++ b/action.php @@ -418,4 +418,4 @@ switch ($VARS['action']) { session_destroy(); header('Location: index.php'); die("Logged out."); -} \ No newline at end of file +} diff --git a/api.php b/api.php index 92e106a..72910f0 100644 --- a/api.php +++ b/api.php @@ -176,4 +176,4 @@ switch ($VARS['action']) { default: http_response_code(404); die("\"404 Action not found\""); -} \ No newline at end of file +} diff --git a/app.php b/app.php index 1442346..d961e30 100644 --- a/app.php +++ b/app.php @@ -18,6 +18,12 @@ if (!is_empty($_GET['page'])) { $pageid = "404"; } } + +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=script", false); +header("Link: ; rel=preload; as=script", false); ?> @@ -43,6 +49,7 @@ if (!is_empty($_GET['page'])) { if (isset(PAGES[$pageid]['styles'])) { foreach (PAGES[$pageid]['styles'] as $style) { echo "\n"; + header("Link: <$style>; rel=preload; as=style", false); } } ?> @@ -169,8 +176,9 @@ END; if (isset(PAGES[$pageid]['scripts'])) { foreach (PAGES[$pageid]['scripts'] as $script) { echo "\n"; + header("Link: <$script>; rel=preload; as=script", false); } } ?> - \ No newline at end of file + diff --git a/index.php b/index.php index 6a8c7e2..419aa46 100644 --- a/index.php +++ b/index.php @@ -72,6 +72,11 @@ if (checkLoginServer()) { } else { $alert = lang("login server unavailable", false); } +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=style", false); +header("Link: ; rel=preload; as=script", false); +header("Link: ; rel=preload; as=script", false); ?> @@ -147,4 +152,4 @@ if (checkLoginServer()) { - \ No newline at end of file + diff --git a/lang/en_us.php b/lang/en_us.php index 38b7168..4985a53 100644 --- a/lang/en_us.php +++ b/lang/en_us.php @@ -157,4 +157,4 @@ define("STRINGS", [ "shift name" => "Shift Name", "workers" => "Workers", "current job" => "Current job: " -]); \ No newline at end of file +]); diff --git a/lib/iputils.php b/lib/iputils.php new file mode 100644 index 0000000..2d03e9a --- /dev/null +++ b/lib/iputils.php @@ -0,0 +1,127 @@ + + */ +function ip4_in_cidr($ip, $cidr) { + if (strpos($cidr, '/') == false) { + $cidr .= '/32'; + } + // $range is in IP/CIDR format eg 127.0.0.1/24 + list( $cidr, $netmask ) = explode('/', $cidr, 2); + $range_decimal = ip2long($cidr); + $ip_decimal = ip2long($ip); + $wildcard_decimal = pow(2, ( 32 - $netmask)) - 1; + $netmask_decimal = ~ $wildcard_decimal; + return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) ); +} + +/** + * Check if a given ipv6 address is in a given cidr + * @param string $ip IP to check in IPV6 format + * @param string $cidr CIDR netmask + * @return boolean true if the IP is in this range, false otherwise. + * @author MW. + */ +function ip6_in_cidr($ip, $cidr) { + $address = inet_pton($ip); + $subnetAddress = inet_pton(explode("/", $cidr)[0]); + $subnetMask = explode("/", $cidr)[1]; + + $addr = str_repeat("f", $subnetMask / 4); + switch ($subnetMask % 4) { + case 0: + break; + case 1: + $addr .= "8"; + break; + case 2: + $addr .= "c"; + break; + case 3: + $addr .= "e"; + break; + } + $addr = str_pad($addr, 32, '0'); + $addr = pack("H*", $addr); + + $binMask = $addr; + return ($address & $binMask) == $subnetAddress; +} + +/** + * Check if the REMOTE_ADDR is on Cloudflare's network. + * @return boolean true if it is, otherwise false + */ +function validateCloudflare() { + if (filter_var($_SERVER["REMOTE_ADDR"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + // Using IPv6 + $cloudflare_ips_v6 = [ + "2400:cb00::/32", + "2405:8100::/32", + "2405:b500::/32", + "2606:4700::/32", + "2803:f800::/32", + "2c0f:f248::/32", + "2a06:98c0::/29" + ]; + $valid = false; + foreach ($cloudflare_ips_v6 as $cidr) { + if (ip6_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) { + $valid = true; + break; + } + } + } else { + // Using IPv4 + $cloudflare_ips_v4 = [ + "103.21.244.0/22", + "103.22.200.0/22", + "103.31.4.0/22", + "104.16.0.0/12", + "108.162.192.0/18", + "131.0.72.0/22", + "141.101.64.0/18", + "162.158.0.0/15", + "172.64.0.0/13", + "173.245.48.0/20", + "188.114.96.0/20", + "190.93.240.0/20", + "197.234.240.0/22", + "198.41.128.0/17" + ]; + $valid = false; + foreach ($cloudflare_ips_v4 as $cidr) { + if (ip4_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) { + $valid = true; + break; + } + } + } + return $valid; +} + +/** + * Makes a good guess at the client's real IP address. + * + * @return string Client IP or `0.0.0.0` if we can't find anything + */ +function getClientIP() { + // If CloudFlare is in the mix, we should use it. + // Check if the request is actually from CloudFlare before trusting it. + if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) { + if (validateCloudflare()) { + return $_SERVER["HTTP_CF_CONNECTING_IP"]; + } + } + + if (isset($_SERVER["REMOTE_ADDR"])) { + return $_SERVER["REMOTE_ADDR"]; + } + + return "0.0.0.0"; // This will not happen unless we aren't a web server +} diff --git a/lib/userinfo.php b/lib/userinfo.php index d7a24e4..3ff3480 100644 --- a/lib/userinfo.php +++ b/lib/userinfo.php @@ -210,4 +210,4 @@ function getGroupsByUsername($username) { } else { return []; } -} \ No newline at end of file +} diff --git a/mobile/index.php b/mobile/index.php index da36c83..516abe1 100644 --- a/mobile/index.php +++ b/mobile/index.php @@ -9,6 +9,10 @@ * Mobile app API */ +// The name of the permission needed to log in. +// Set to null if you don't need it. +$access_permission = "QWIKCLOCK"; + require __DIR__ . "/../required.php"; require __DIR__ . "/../lib/login.php"; @@ -93,7 +97,7 @@ switch ($VARS['action']) { if (user_exists($VARS['username'])) { if (get_account_status($VARS['username']) == "NORMAL") { if (authenticate_user($VARS['username'], $VARS['password'], $autherror)) { - if (account_has_permission($VARS['username'], "QWIKCLOCK")) { + if (is_null($access_permission) || account_has_permission($VARS['username'], $access_permission)) { doLoginUser($VARS['username'], $VARS['password']); $_SESSION['mobile'] = true; exit(json_encode(["status" => "OK"])); @@ -107,4 +111,4 @@ switch ($VARS['action']) { default: http_response_code(404); die(json_encode(["status" => "ERROR", "msg" => "The requested action is not available."])); -} \ No newline at end of file +} diff --git a/pages/404.php b/pages/404.php index 21110a2..1e89266 100644 --- a/pages/404.php +++ b/pages/404.php @@ -9,4 +9,4 @@

- \ No newline at end of file + diff --git a/pages/home.php b/pages/home.php index 72c2089..52ef16f 100644 --- a/pages/home.php +++ b/pages/home.php @@ -61,4 +61,4 @@ redirectifnotloggedin(); - \ No newline at end of file + diff --git a/required.php b/required.php index 73d6795..5a7b6fd 100644 --- a/required.php +++ b/required.php @@ -11,10 +11,12 @@ ob_start(); // allow sending headers after content // Unicode, solves almost all stupid encoding problems header('Content-Type: text/html; charset=utf-8'); -// l33t $ecurity h4x +// Strip PHP version +header('X-Powered-By: PHP'); + +// Security header('X-Content-Type-Options: nosniff'); header('X-XSS-Protection: 1; mode=block'); -header('X-Powered-By: PHP'); // no versions makes it harder to find vulns header('X-Frame-Options: "DENY"'); header('Referrer-Policy: "no-referrer, strict-origin-when-cross-origin"'); $SECURE_NONCE = base64_encode(random_bytes(8)); diff --git a/settings.template.php b/settings.template.php index 5f449ec..ff4af4d 100644 --- a/settings.template.php +++ b/settings.template.php @@ -64,4 +64,4 @@ define('LANGUAGE', "en_us"); define("FOOTER_TEXT", ""); -define("COPYRIGHT_NAME", "Netsyms Technologies"); \ No newline at end of file +define("COPYRIGHT_NAME", "Netsyms Technologies"); diff --git a/static/css/app.css b/static/css/app.css index 9fa40e1..c70fd8c 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -54,4 +54,4 @@ body { #seconds_bar_line { background-color: #ffc107; height: 5px; -} \ No newline at end of file +} diff --git a/static/js/app.js b/static/js/app.js index f5358bb..9ecb5a3 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -27,4 +27,4 @@ function getniceurl() { url = url.substring(url.lastIndexOf("/") + 1); url = url.replace(/&?msg=([^&]$|[^&]*)/i, ""); return url; -} \ No newline at end of file +}