diff --git a/README.md b/README.md index fa84578..2c60e9f 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,22 @@ AI scrapers are everywhere. This will stop them. `robots.txt` won't. - Protect your endpoint from AI bots with a cryptographic challenge - Easy configuration in jsonc - Support for cloudflare +- Support for IP-Range based rules (both ipv4 and ipv6) - Minimal. The waiting page is tiny and light on network usage. ### Planned features -- Dynamic challenge amount (aka difficulty) +- Dynamic challenge amount (aka difficulty) based on traffic - Detection of token overuse - Better wait screen - Better git integration (it's quite rudimentary right now) ## Caveats If you are using this, it's almost certain search engines will stop indexing your site. Keep this in mind. + +## Setup guide + +1. Clone and build this repo. You will need `openssl`, `g++>=12`, and deps for `pistache`, `fmt` and `tinylates`. +2. Create a `config.jsonc`. An example one is in `example/`. +3. Adjust the config to your needs. Options are documented with comments in the example config. +4. Set up your IP rules if you want. These allow you to set up IPs that are automatically blocked, or allowed to access without a challenge. This is useful for e.g. search engine scrapers. Some IP ranges can be found in `example/index_bots.jsonc`. +5. Run checkpoint with your config: `./build/checkpoint -c config.jsonc`. How you run it long-term as a service is up to you. diff --git a/example/config.jsonc b/example/config.jsonc index ae87760..a90743d 100644 --- a/example/config.jsonc +++ b/example/config.jsonc @@ -2,13 +2,14 @@ // where the html files are located "html_dir": "./html", - // where the proxy should store its db (directory) + // where the proxy should store its private signing key (directory) "data_dir": "./data", // what port the proxy should listen on "port": 3001, - // what address should the proxy pass after successful verification + // what address should the proxy pass after successful verification. DO NOT add a / at the end or http(s)://. + // this address should be local (127.0.0.1). Other configurations are not supported. "forward_address": "127.0.0.1:3000", // max request size of 10MB @@ -18,5 +19,21 @@ "proxy_timeout_sec": 120, // enables (a lot) more logging - "trace_logging": false + "trace_logging": false, + + // the default difficulty for the challenge. 4 takes less than a second on a powerful desktop, and up to 15s on a low-powered phone. + // 5 is 16x slower. + // NOT recommended to set to anything below 4 or above 5. + "default_challenge_difficulty": 4, + + // specific ip range configs. + "ip_configs": [ + { + "action": "ALLOW", + "ip_ranges": [ + "127.0.0.1/24", + "::1/128" + ] + } + ] } \ No newline at end of file diff --git a/example/index_bots.jsonc b/example/index_bots.jsonc new file mode 100644 index 0000000..76d6d6f --- /dev/null +++ b/example/index_bots.jsonc @@ -0,0 +1,311 @@ +// This jsonc file contains some known web crawlers used by search engines. +// You may pick and choose whichever you like. +[ + // Google, gathered 15 IV 2025 + { + "action": "Allow", + "ip_ranges": [ + "2001:4860:4801:10::/64", + "2001:4860:4801:11::/64", + "2001:4860:4801:12::/64", + "2001:4860:4801:13::/64", + "2001:4860:4801:14::/64", + "2001:4860:4801:15::/64", + "2001:4860:4801:16::/64", + "2001:4860:4801:17::/64", + "2001:4860:4801:18::/64", + "2001:4860:4801:19::/64", + "2001:4860:4801:1a::/64", + "2001:4860:4801:1b::/64", + "2001:4860:4801:1c::/64", + "2001:4860:4801:1d::/64", + "2001:4860:4801:1e::/64", + "2001:4860:4801:1f::/64", + "2001:4860:4801:20::/64", + "2001:4860:4801:21::/64", + "2001:4860:4801:22::/64", + "2001:4860:4801:23::/64", + "2001:4860:4801:24::/64", + "2001:4860:4801:25::/64", + "2001:4860:4801:26::/64", + "2001:4860:4801:27::/64", + "2001:4860:4801:28::/64", + "2001:4860:4801:29::/64", + "2001:4860:4801:2::/64", + "2001:4860:4801:2a::/64", + "2001:4860:4801:2b::/64", + "2001:4860:4801:2c::/64", + "2001:4860:4801:2d::/64", + "2001:4860:4801:2e::/64", + "2001:4860:4801:2f::/64", + "2001:4860:4801:31::/64", + "2001:4860:4801:32::/64", + "2001:4860:4801:33::/64", + "2001:4860:4801:34::/64", + "2001:4860:4801:35::/64", + "2001:4860:4801:36::/64", + "2001:4860:4801:37::/64", + "2001:4860:4801:38::/64", + "2001:4860:4801:39::/64", + "2001:4860:4801:3a::/64", + "2001:4860:4801:3b::/64", + "2001:4860:4801:3c::/64", + "2001:4860:4801:3d::/64", + "2001:4860:4801:3e::/64", + "2001:4860:4801:40::/64", + "2001:4860:4801:41::/64", + "2001:4860:4801:42::/64", + "2001:4860:4801:43::/64", + "2001:4860:4801:44::/64", + "2001:4860:4801:45::/64", + "2001:4860:4801:46::/64", + "2001:4860:4801:47::/64", + "2001:4860:4801:48::/64", + "2001:4860:4801:49::/64", + "2001:4860:4801:4a::/64", + "2001:4860:4801:4b::/64", + "2001:4860:4801:4c::/64", + "2001:4860:4801:50::/64", + "2001:4860:4801:51::/64", + "2001:4860:4801:52::/64", + "2001:4860:4801:53::/64", + "2001:4860:4801:54::/64", + "2001:4860:4801:55::/64", + "2001:4860:4801:56::/64", + "2001:4860:4801:60::/64", + "2001:4860:4801:61::/64", + "2001:4860:4801:62::/64", + "2001:4860:4801:63::/64", + "2001:4860:4801:64::/64", + "2001:4860:4801:65::/64", + "2001:4860:4801:66::/64", + "2001:4860:4801:67::/64", + "2001:4860:4801:68::/64", + "2001:4860:4801:69::/64", + "2001:4860:4801:6a::/64", + "2001:4860:4801:6b::/64", + "2001:4860:4801:6c::/64", + "2001:4860:4801:6d::/64", + "2001:4860:4801:6e::/64", + "2001:4860:4801:6f::/64", + "2001:4860:4801:70::/64", + "2001:4860:4801:71::/64", + "2001:4860:4801:72::/64", + "2001:4860:4801:73::/64", + "2001:4860:4801:74::/64", + "2001:4860:4801:75::/64", + "2001:4860:4801:76::/64", + "2001:4860:4801:77::/64", + "2001:4860:4801:78::/64", + "2001:4860:4801:79::/64", + "2001:4860:4801:80::/64", + "2001:4860:4801:81::/64", + "2001:4860:4801:82::/64", + "2001:4860:4801:83::/64", + "2001:4860:4801:84::/64", + "2001:4860:4801:85::/64", + "2001:4860:4801:86::/64", + "2001:4860:4801:87::/64", + "2001:4860:4801:88::/64", + "2001:4860:4801:90::/64", + "2001:4860:4801:91::/64", + "2001:4860:4801:92::/64", + "2001:4860:4801:93::/64", + "2001:4860:4801:94::/64", + "2001:4860:4801:95::/64", + "2001:4860:4801:96::/64", + "2001:4860:4801:a0::/64", + "2001:4860:4801:a1::/64", + "2001:4860:4801:a2::/64", + "2001:4860:4801:a3::/64", + "2001:4860:4801:a4::/64", + "2001:4860:4801:a5::/64", + "2001:4860:4801:c::/64", + "2001:4860:4801:f::/64", + "192.178.5.0/27", + "192.178.6.0/27", + "192.178.6.128/27", + "192.178.6.160/27", + "192.178.6.192/27", + "192.178.6.32/27", + "192.178.6.64/27", + "192.178.6.96/27", + "34.100.182.96/28", + "34.101.50.144/28", + "34.118.254.0/28", + "34.118.66.0/28", + "34.126.178.96/28", + "34.146.150.144/28", + "34.147.110.144/28", + "34.151.74.144/28", + "34.152.50.64/28", + "34.154.114.144/28", + "34.155.98.32/28", + "34.165.18.176/28", + "34.175.160.64/28", + "34.176.130.16/28", + "34.22.85.0/27", + "34.64.82.64/28", + "34.65.242.112/28", + "34.80.50.80/28", + "34.88.194.0/28", + "34.89.10.80/28", + "34.89.198.80/28", + "34.96.162.48/28", + "35.247.243.240/28", + "66.249.64.0/27", + "66.249.64.128/27", + "66.249.64.160/27", + "66.249.64.224/27", + "66.249.64.32/27", + "66.249.64.64/27", + "66.249.64.96/27", + "66.249.65.0/27", + "66.249.65.128/27", + "66.249.65.160/27", + "66.249.65.192/27", + "66.249.65.224/27", + "66.249.65.32/27", + "66.249.65.64/27", + "66.249.65.96/27", + "66.249.66.0/27", + "66.249.66.128/27", + "66.249.66.160/27", + "66.249.66.192/27", + "66.249.66.224/27", + "66.249.66.32/27", + "66.249.66.64/27", + "66.249.66.96/27", + "66.249.68.0/27", + "66.249.68.128/27", + "66.249.68.32/27", + "66.249.68.64/27", + "66.249.68.96/27", + "66.249.69.0/27", + "66.249.69.128/27", + "66.249.69.160/27", + "66.249.69.192/27", + "66.249.69.224/27", + "66.249.69.32/27", + "66.249.69.64/27", + "66.249.69.96/27", + "66.249.70.0/27", + "66.249.70.128/27", + "66.249.70.160/27", + "66.249.70.192/27", + "66.249.70.224/27", + "66.249.70.32/27", + "66.249.70.64/27", + "66.249.70.96/27", + "66.249.71.0/27", + "66.249.71.128/27", + "66.249.71.160/27", + "66.249.71.192/27", + "66.249.71.224/27", + "66.249.71.32/27", + "66.249.71.64/27", + "66.249.71.96/27", + "66.249.72.0/27", + "66.249.72.128/27", + "66.249.72.160/27", + "66.249.72.192/27", + "66.249.72.224/27", + "66.249.72.32/27", + "66.249.72.64/27", + "66.249.72.96/27", + "66.249.73.0/27", + "66.249.73.128/27", + "66.249.73.160/27", + "66.249.73.192/27", + "66.249.73.224/27", + "66.249.73.32/27", + "66.249.73.64/27", + "66.249.73.96/27", + "66.249.74.0/27", + "66.249.74.128/27", + "66.249.74.160/27", + "66.249.74.192/27", + "66.249.74.32/27", + "66.249.74.64/27", + "66.249.74.96/27", + "66.249.75.0/27", + "66.249.75.128/27", + "66.249.75.160/27", + "66.249.75.192/27", + "66.249.75.224/27", + "66.249.75.32/27", + "66.249.75.64/27", + "66.249.75.96/27", + "66.249.76.0/27", + "66.249.76.128/27", + "66.249.76.160/27", + "66.249.76.192/27", + "66.249.76.224/27", + "66.249.76.32/27", + "66.249.76.64/27", + "66.249.76.96/27", + "66.249.77.0/27", + "66.249.77.128/27", + "66.249.77.160/27", + "66.249.77.192/27", + "66.249.77.224/27", + "66.249.77.32/27", + "66.249.77.64/27", + "66.249.77.96/27", + "66.249.78.0/27", + "66.249.78.32/27", + "66.249.79.0/27", + "66.249.79.128/27", + "66.249.79.160/27", + "66.249.79.192/27", + "66.249.79.224/27", + "66.249.79.32/27", + "66.249.79.64/27", + "66.249.79.96/27" + ] + }, + // Bing, retrieved 15 IV 2025 + { + "action": "Allow", + "ip_ranges": [ + "157.55.39.0/24", + "207.46.13.0/24", + "40.77.167.0/24", + "13.66.139.0/24", + "13.66.144.0/24", + "52.167.144.0/24", + "13.67.10.16/28", + "13.69.66.240/28", + "13.71.172.224/28", + "139.217.52.0/28", + "191.233.204.224/28", + "20.36.108.32/28", + "20.43.120.16/28", + "40.79.131.208/28", + "40.79.186.176/28", + "52.231.148.0/28", + "20.79.107.240/28", + "51.105.67.0/28", + "20.125.163.80/28", + "40.77.188.0/22", + "65.55.210.0/24", + "199.30.24.0/23", + "40.77.202.0/24", + "40.77.139.0/25", + "20.74.197.0/28", + "20.15.133.160/27", + "40.77.177.0/24", + "40.77.178.0/23" + ] + }, + { + // Kagi, retrieved 15 IV 2025 + "action": "Allow", + "ip_ranges": [ + "216.18.205.234/32", + "35.212.27.76/32", + "104.254.65.50/32", + "209.151.156.194/32" + ] + } +] \ No newline at end of file diff --git a/src/config/Config.cpp b/src/config/Config.cpp index d9d170d..d3824c5 100644 --- a/src/config/Config.cpp +++ b/src/config/Config.cpp @@ -5,6 +5,18 @@ #include "../helpers/FsUtils.hpp" #include "../GlobalState.hpp" +static CConfig::eConfigIPAction strToAction(const std::string& s) { + // TODO: allow any case I'm lazy it's 1am + if (s == "ALLOW" || s == "allow" || s == "Allow") + return CConfig::IP_ACTION_ALLOW; + if (s == "Deny" || s == "deny" || s == "Deny") + return CConfig::IP_ACTION_DENY; + if (s == "CHALLENGE" || s == "challenge" || s == "Challenge") + return CConfig::IP_ACTION_CHALLENGE; + + throw std::runtime_error("Invalid ip config action"); +} + CConfig::CConfig() { auto json = glz::read_jsonc( NFsUtils::readFileAsString(NFsUtils::isAbsolute(g_pGlobalState->configPath) ? g_pGlobalState->configPath : g_pGlobalState->cwd + "/" + g_pGlobalState->configPath).value()); @@ -13,4 +25,17 @@ CConfig::CConfig() { throw std::runtime_error("No config / bad config format"); m_config = json.value(); + + // parse some datas + for (const auto& ic : m_config.ip_configs) { + SIPRangeConfigParsed parsed; + parsed.action = strToAction(ic.action); + parsed.difficulty = ic.difficulty; + + for (const auto& ir : ic.ip_ranges) { + parsed.ip_ranges.emplace_back(CIPRange(ir)); + } + + m_parsedConfigDatas.ip_configs.emplace_back(std::move(parsed)); + } } \ No newline at end of file diff --git a/src/config/Config.hpp b/src/config/Config.hpp index 7f9103c..9b39286 100644 --- a/src/config/Config.hpp +++ b/src/config/Config.hpp @@ -3,20 +3,46 @@ #include #include +#include "IPRange.hpp" + class CConfig { public: CConfig(); + enum eConfigIPAction : uint8_t { + IP_ACTION_DENY = 0, + IP_ACTION_ALLOW, + IP_ACTION_CHALLENGE + }; + + struct SIPRangeConfig { + std::string action = ""; + std::vector ip_ranges; + int difficulty = -1; + }; + + struct SIPRangeConfigParsed { + eConfigIPAction action = IP_ACTION_DENY; + std::vector ip_ranges; + int difficulty = -1; + }; + struct SConfig { - int port = 3001; - std::string forward_address = "127.0.0.1:3000"; - std::string data_dir = ""; - std::string html_dir = ""; - unsigned long int max_request_size = 10000000; // 10MB - bool git_host = false; - unsigned long int proxy_timeout_sec = 120; // 2 minutes - bool trace_logging = false; + int port = 3001; + std::string forward_address = "127.0.0.1:3000"; + std::string data_dir = ""; + std::string html_dir = ""; + unsigned long int max_request_size = 10000000; // 10MB + bool git_host = false; + unsigned long int proxy_timeout_sec = 120; // 2 minutes + bool trace_logging = false; + std::vector ip_configs; + int default_challenge_difficulty = 4; } m_config; + + struct { + std::vector ip_configs; + } m_parsedConfigDatas; }; inline std::unique_ptr g_pConfig; \ No newline at end of file diff --git a/src/config/IPRange.cpp b/src/config/IPRange.cpp new file mode 100644 index 0000000..901d1d4 --- /dev/null +++ b/src/config/IPRange.cpp @@ -0,0 +1,129 @@ +#include "IPRange.hpp" + +#include +#include + +CIP::CIP(const std::string& ip) { + if (std::count(ip.begin(), ip.end(), '.') == 3) + parseV4(ip); + else if (std::count(ip.begin(), ip.end(), ':') >= 2) + parseV6(ip); + else + throw std::runtime_error("IP not valid"); +} + +void CIP::parseV4(const std::string& ip) { + m_v6 = false; + + std::string_view curr; + size_t lastPos = 0; + auto advance = [&]() { + size_t prev = lastPos ? lastPos + 1 : lastPos; + lastPos = ip.find('.', prev); + + if (lastPos == std::string::npos) + curr = std::string_view{ip}.substr(prev); + else + curr = std::string_view{ip}.substr(prev, lastPos - prev); + }; + + for (size_t i = 0; i < 4; ++i) { + advance(); + m_blocks.push_back(std::stoul(std::string{curr})); + + if (m_blocks.back() > 0xFF) + throw std::runtime_error("Invalid IPv4 byte"); + } +} + +void CIP::parseV6(const std::string& ip) { + const auto COLONS = std::count(ip.begin(), ip.end(), ':'); + + m_v6 = true; + + std::string_view curr; + size_t lastPos = 0; + bool first = true; + auto advance = [&]() { + size_t prev = !first ? lastPos + 1 : lastPos; + lastPos = ip.find(':', prev); + + if (lastPos == std::string::npos) + curr = std::string_view{ip}.substr(prev); + else + curr = std::string_view{ip}.substr(prev, lastPos - prev); + + first = false; + }; + + for (size_t i = 0; i < 8; ++i) { + advance(); + if (curr.empty()) { + for (size_t j = 0; j < 8 - COLONS; ++j) { + i++; + m_blocks.push_back(0); + } + + if (ip.starts_with("::") || ip.ends_with("::")) { + m_blocks.push_back(0); + advance(); + } else + i--; + continue; + } else + m_blocks.push_back(std::stoul(std::string{curr}, nullptr, 16)); + + if (m_blocks.back() > 0xFFFF) + throw std::runtime_error("Invalid IPv6 byte"); + } +} + +CIPRange::CIPRange(const std::string& range) { + if (!range.contains('/')) + throw std::runtime_error("Range has no subnet"); + + m_subnet = std::stoul(range.substr(range.find('/') + 1)); + + m_ip = CIP(range.substr(0, range.find('/'))); +} + +bool CIPRange::ipMatches(const CIP& ip) const { + if (m_ip.m_v6 != ip.m_v6) + return false; + + if (m_ip.m_v6) + return ipMatchesV6(ip); + return ipMatchesV4(ip); +} + +bool CIPRange::ipMatchesV4(const CIP& ip) const { + uint32_t rangeMask = 0xFFFFFFFF << (32 - m_subnet); + uint32_t rangeIP = + (((uint32_t)m_ip.m_blocks.at(0)) << 24) | (((uint32_t)m_ip.m_blocks.at(1)) << 16) | (((uint32_t)m_ip.m_blocks.at(2)) << 8) | (((uint32_t)m_ip.m_blocks.at(3)) << 0); + uint32_t incomingIP = + (((uint32_t)ip.m_blocks.at(0)) << 24) | (((uint32_t)ip.m_blocks.at(1)) << 16) | (((uint32_t)ip.m_blocks.at(2)) << 8) | (((uint32_t)ip.m_blocks.at(3)) << 0); + + return (rangeMask & rangeIP) == (rangeMask & incomingIP); +} + +bool CIPRange::ipMatchesV6(const CIP& ip) const { + uint64_t rangeMaskLeft = 0xFFFFFFFFFFFFFFFF << (m_subnet > 64 ? 0 : 64 - m_subnet); + uint64_t rangeIPLeft = + (((uint64_t)m_ip.m_blocks.at(0)) << 48) | (((uint64_t)m_ip.m_blocks.at(1)) << 32) | (((uint64_t)m_ip.m_blocks.at(2)) << 16) | (((uint64_t)m_ip.m_blocks.at(3)) << 0); + uint64_t incomingIPLeft = + (((uint64_t)ip.m_blocks.at(0)) << 48) | (((uint64_t)ip.m_blocks.at(1)) << 32) | (((uint64_t)ip.m_blocks.at(2)) << 16) | (((uint64_t)ip.m_blocks.at(3)) << 0); + + if ((rangeMaskLeft & rangeIPLeft) != (rangeMaskLeft & incomingIPLeft)) + return false; + + if (m_subnet <= 64) + return true; + + uint64_t rangeMaskRight = 0xFFFFFFFFFFFFFFFF << (/* m_subnet > 64 */ 128 - m_subnet); + uint64_t rangeIPRight = + (((uint64_t)m_ip.m_blocks.at(4)) << 48) | (((uint64_t)m_ip.m_blocks.at(5)) << 32) | (((uint64_t)m_ip.m_blocks.at(6)) << 16) | (((uint64_t)m_ip.m_blocks.at(7)) << 0); + uint64_t incomingIPRight = + (((uint64_t)ip.m_blocks.at(4)) << 48) | (((uint64_t)ip.m_blocks.at(5)) << 32) | (((uint64_t)ip.m_blocks.at(6)) << 16) | (((uint64_t)ip.m_blocks.at(7)) << 0); + + return (rangeMaskRight & rangeIPRight) == (rangeMaskRight & incomingIPRight); +} \ No newline at end of file diff --git a/src/config/IPRange.hpp b/src/config/IPRange.hpp new file mode 100644 index 0000000..02c8602 --- /dev/null +++ b/src/config/IPRange.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +class CIP { + public: + CIP() = default; + CIP(const std::string& ip); + + bool m_v6 = false; + std::vector m_blocks; + + private: + void parseV4(const std::string& ip); + void parseV6(const std::string& ip); +}; + +// Accepts both ipv4 and ipv6 +class CIPRange { + public: + CIPRange(const std::string& range); + + bool ipMatches(const CIP& ip) const; + + private: + CIP m_ip; + size_t m_subnet = 0; + + bool ipMatchesV6(const CIP& ip) const; + bool ipMatchesV4(const CIP& ip) const; +}; \ No newline at end of file diff --git a/src/core/Handler.cpp b/src/core/Handler.cpp index 65a0ee1..e4fb207 100644 --- a/src/core/Handler.cpp +++ b/src/core/Handler.cpp @@ -213,6 +213,40 @@ void CServerHandler::onRequest(const Pistache::Http::Request& req, Pistache::Htt } } + int challengeDifficulty = g_pConfig->m_config.default_challenge_difficulty; + + if (!g_pConfig->m_parsedConfigDatas.ip_configs.empty()) { + const auto IP = CIP(req.address().host()); + for (const auto& ic : g_pConfig->m_parsedConfigDatas.ip_configs) { + bool matched = false; + + for (const auto& ipr : ic.ip_ranges) { + if (!ipr.ipMatches(IP)) + continue; + + matched = true; + break; + } + + if (matched) { + if (ic.action == CConfig::IP_ACTION_ALLOW) { + Debug::log(LOG, " | Action: PASS (ip rule matched for {})", req.address().host()); + proxyPass(req, response); + return; + } else if (ic.action == CConfig::IP_ACTION_DENY) { + Debug::log(LOG, " | Action: DENY (ip rule matched for {})", req.address().host()); + response.send(Pistache::Http::Code::Forbidden, "Forbidden"); + return; + } + + // if it's challenge then it's default so just set the difficulty if applicable and proceed + if (ic.difficulty != -1) + challengeDifficulty = ic.difficulty; + break; + } + } + } + if (req.cookies().has(TOKEN_COOKIE_NAME)) { // check the token const auto TOKEN = CToken(req.cookies().get(TOKEN_COOKIE_NAME).value); @@ -234,7 +268,7 @@ void CServerHandler::onRequest(const Pistache::Http::Request& req, Pistache::Htt } else Debug::log(LOG, " | Action: CHALLENGE (no token)"); - serveStop(req, response); + serveStop(req, response, challengeDifficulty); } void CServerHandler::onTimeout(const Pistache::Http::Request& request, Pistache::Http::ResponseWriter response) { @@ -274,18 +308,16 @@ void CServerHandler::challengeSubmitted(const Pistache::Http::Request& req, Pist response.send(Pistache::Http::Code::Ok, "Ok"); } -void CServerHandler::serveStop(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response) { +void CServerHandler::serveStop(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response, int difficulty) { static const auto PAGE_INDEX = NFsUtils::readFileAsString(NFsUtils::htmlPath("/index.min.html")).value(); static const auto PAGE_ROOT = PAGE_INDEX.substr(0, PAGE_INDEX.find_last_of("/") + 1); CTinylates page(PAGE_INDEX); page.setTemplateRoot(PAGE_ROOT); - const auto NONCE = generateNonce(); - const auto DIFFICULTY = 4; + const auto NONCE = generateNonce(); + const auto CHALLENGE = CChallenge(fingerprintForRequest(req), NONCE, difficulty); - const auto CHALLENGE = CChallenge(fingerprintForRequest(req), NONCE, DIFFICULTY); - - page.add("challengeDifficulty", CTinylatesProp(std::to_string(DIFFICULTY))); + page.add("challengeDifficulty", CTinylatesProp(std::to_string(difficulty))); page.add("challengeNonce", CTinylatesProp(NONCE)); page.add("challengeSignature", CTinylatesProp(CHALLENGE.signature())); page.add("challengeFingerprint", CTinylatesProp(CHALLENGE.fingerprint())); diff --git a/src/core/Handler.hpp b/src/core/Handler.hpp index e779313..87f1752 100644 --- a/src/core/Handler.hpp +++ b/src/core/Handler.hpp @@ -16,7 +16,7 @@ class CServerHandler : public Pistache::Http::Handler { void onTimeout(const Pistache::Http::Request& request, Pistache::Http::ResponseWriter response); private: - void serveStop(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response); + void serveStop(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response, int difficulty); void proxyPass(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response); void challengeSubmitted(const Pistache::Http::Request& req, Pistache::Http::ResponseWriter& response); std::string fingerprintForRequest(const Pistache::Http::Request& req);