diff --git a/.gitignore b/.gitignore index df78ea5..c583050 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ vendor settings.php nbproject/private database.mwb.bak -*.sync-conflict* \ No newline at end of file +*.sync-conflict* diff --git a/README.md b/README.md index d2b27a2..79ac384 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ BinStack ======== -BinStack is the easy way to keep track of your business assets. +BinStack is the easy way to keep track of your business assets. Never wander in search of the stapler again. https://netsyms.biz/apps/binstack @@ -9,20 +9,20 @@ https://netsyms.biz/apps/binstack Features -------- -**Asset Tracking** -Easily manage locations, tag numbers, assigned users, and other important item +**Asset Tracking** +Easily manage locations, tag numbers, assigned users, and other important item properties. -**Fully Searchable** -Start typing in the search box to instantly filter hundreds of results down to +**Fully Searchable** +Start typing in the search box to instantly filter hundreds of results down to only a few. Sorting also built in. -**Autofill** -You don't need to remember category codes or usernames. Just start typing and +**Autofill** +You don't need to remember category codes or usernames. Just start typing and BinStack will figure out what you're looking for. -**Mobile-ready** -Take inventory or find what you need on the go. BinStack looks and works +**Mobile-ready** +Take inventory or find what you need on the go. BinStack looks and works great on modern smartphones and tablets. Installing diff --git a/action.php b/action.php index 7ccb0f2..33ddd94 100644 --- a/action.php +++ b/action.php @@ -223,4 +223,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 fe0015e..e37bed9 100644 --- a/api.php +++ b/api.php @@ -38,4 +38,4 @@ switch ($VARS['action']) { default: header("HTTP/1.1 400 Bad Request"); die("\"400 Bad Request\""); -} \ No newline at end of file +} diff --git a/app.php b/app.php index abd25cf..3818f86 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/lib/iputils.php b/lib/iputils.php new file mode 100644 index 0000000..f46c89f --- /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/login.php b/lib/login.php index 13d5671..aa337d3 100644 --- a/lib/login.php +++ b/lib/login.php @@ -40,6 +40,33 @@ function checkLoginServer() { } } +/** + * Checks if the given AccountHub API key is valid by attempting to + * access the API with it. + * @param String $key The API key to check + * @return boolean TRUE if the key is valid, FALSE if invalid or something went wrong + */ +function checkAPIKey($key) { + try { + $client = new GuzzleHttp\Client(); + + $response = $client + ->request('POST', PORTAL_API, [ + 'form_params' => [ + 'key' => $key, + 'action' => "ping" + ] + ]); + + if ($response->getStatusCode() === 200) { + return true; + } + return false; + } catch (Exception $e) { + return false; + } +} + //////////////////////////////////////////////////////////////////////////////// // Account handling // //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/userinfo.php b/lib/userinfo.php index 8d63990..65f6b38 100644 --- a/lib/userinfo.php +++ b/lib/userinfo.php @@ -124,4 +124,4 @@ function getManagedUIDs($manageruid) { } else { return []; } -} \ No newline at end of file +} diff --git a/mobile/index.php b/mobile/index.php index 85ca5d6..d8e5623 100644 --- a/mobile/index.php +++ b/mobile/index.php @@ -1,14 +1,13 @@ "OK"])); @@ -107,4 +106,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/home.php b/pages/home.php index 41106bb..91f1d9f 100644 --- a/pages/home.php +++ b/pages/home.php @@ -52,4 +52,4 @@ redirectifnotloggedin(); - \ No newline at end of file + diff --git a/required.php b/required.php index 1fa5faf..7340b54 100644 --- a/required.php +++ b/required.php @@ -12,10 +12,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)); @@ -142,6 +144,7 @@ function lang($key, $echo = true) { if (array_key_exists($key, STRINGS)) { $str = STRINGS[$key]; } else { + trigger_error("Language key \"$key\" does not exist in " . LANGUAGE, E_USER_WARNING); $str = $key; } @@ -164,6 +167,7 @@ function lang2($key, $replace, $echo = true) { if (array_key_exists($key, STRINGS)) { $str = STRINGS[$key]; } else { + trigger_error("Language key \"$key\" does not exist in " . LANGUAGE, E_USER_WARNING); $str = $key; } diff --git a/settings.template.php b/settings.template.php index d6495e9..c2598d8 100644 --- a/settings.template.php +++ b/settings.template.php @@ -54,4 +54,4 @@ define('LANGUAGE', "en_us"); define("FOOTER_TEXT", ""); define("COPYRIGHT_NAME", "Netsyms Technologies"); -////////////////////////////////////////////////////////////// \ No newline at end of file +////////////////////////////////////////////////////////////// diff --git a/static/css/app.css b/static/css/app.css index 4419fc1..651e07c 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -65,4 +65,4 @@ body { .mobile-app-display { display: none; -} \ No newline at end of file +} diff --git a/static/js/app.js b/static/js/app.js index 2fe1a03..602807d 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -23,4 +23,4 @@ try { window.history.replaceState("", "", getniceurl()); } catch (ex) { -} \ No newline at end of file +}