diff --git a/.env.example b/.env.example index a5a1c66..f69697e 100644 --- a/.env.example +++ b/.env.example @@ -80,7 +80,8 @@ NODE_ENV=production # CAPTCHA_MODE=none # POW_TOKEN_HOURS=24 # POW_TOKEN_MAX_IPS=2 -# POW-DIFFICULTY_LEVEL=low +# POW_DIFFICULTY_LEVEL=low +# POW_CHALLENGE_TIMEOUT=30 # ------------------------------------------------------------------------------ # Optional settings for user management, access control, and quota enforcement: diff --git a/docs/pow-captcha.md b/docs/pow-captcha.md index 1ca9eae..6397271 100644 --- a/docs/pow-captcha.md +++ b/docs/pow-captcha.md @@ -4,11 +4,13 @@ You can require users to complete a proof-of-work before they can access the proxy. This can increase the cost of denial of service attacks and slow down automated abuse. -When configured, users access the challenge UI and request a proof of work. The -server will generate a challenge according to the difficulty level you have set. -The user can then start the worker to solve the challenge. Once the challenge is -solved, the user can submit the solution to the server. The server will verify -the solution and issue a temporary token for that user. +When configured, users access the challenge UI and request a token. The server +sends a challenge to the client, which asks the user's browser to find a +solution to the challenge that meets a certain constraint (the difficulty +level). Once the user has found a solution, they can submit it to the server +and get a user token valid for a period you specify. + +The proof-of-work challenge uses the argon2id hash function. ## Configuration @@ -21,37 +23,73 @@ CAPTCHA_MODE=proof_of_work POW_TOKEN_HOURS=24 # Max number of IPs that can use a user_token issued via proof-of-work POW_TOKEN_MAX_IPS=2 -# The difficulty level of the proof-of-work challenge +# The difficulty level of the proof-of-work challenge. You can use one of the +# predefined levels specified below, or you can specify a custom number of +# expected hash iterations. POW_DIFFICULTY_LEVEL=low ``` ## Difficulty Levels -The difficulty level controls how long it takes to solve the proof-of-work, -specifically by adjusting the average number of iterations required to find a -valid solution. Due to randomness, the actual number of iterations required can -vary significantly. +The difficulty level controls how long, on average, it will take for a user to +solve the proof-of-work challenge. Due to randomness, the actual time can very +significantly; lucky users may solve the challenge in a fraction of the average +time, while unlucky users may take much longer. -You can adjust the difficulty while the proxy is running from the admin interface. +The difficulty level doesn't affect the speed of the hash function itself, only +the number of hashes that will need to be computed. Therefore, the time required +to complete the challenge scales linearly with the difficulty level's iteration +count. -### Extreme +You can adjust the difficulty level while the proxy is running from the admin +interface. -- Average of 4000 iterations required -- Not recommended unless you are expecting very high levels of abuse - -### High - -- Average of 1900 iterations required - -### Medium - -- Average of 900 iterations required +Be aware that there is a time limit for solving the challenge, by default set to +30 minutes. Above 'high' difficulty, you will probably need to increase the time +limit or it will be very hard for users with slow devices to find a solution +within the time limit. ### Low - Average of 200 iterations required - Default setting. +### Medium + +- Average of 900 iterations required + +### High + +- Average of 1900 iterations required + +### Extreme + +- Average of 4000 iterations required +- Not recommended unless you are expecting very high levels of abuse +- May require increasing `POW_CHALLENGE_TIMEOUT` + +### Custom + +Setting `POW_DIFFICULTY_LEVEL` to an integer will use that number of iterations +as the difficulty level. + +## Other challenge settings + +- `POW_CHALLENGE_TIMEOUT`: The time limit for solving the challenge, in minutes. + Default is 30. +- `POW_TOKEN_HOURS`: The period of time for which a user token issued via proof- + of-work can be used. Default is 24 hours. Starts when the challenge is solved. +- `POW_TOKEN_MAX_IPS`: The maximum number of unique IPs that can use a single + user token issued via proof-of-work. Default is 2. +- `POW_TOKEN_PURGE_HOURS`: The period of time after which an expired user token + issued via proof-of-work will be removed from the database. Until it is + purged, users can refresh expired tokens by completing a half-difficulty + challenge. Default is 48 hours. +- `POW_MAX_TOKENS_PER_IP`: The maximum number of active user tokens that can + be associated with a single IP address. After this limit is reached, the + oldest token will be forcibly expired when a new token is issued. Set to 0 + to disable this feature. Default is 0. + ## Custom argon2id parameters You can set custom argon2id parameters for the proof-of-work challenge. diff --git a/src/admin/web/manage.ts b/src/admin/web/manage.ts index b47fe07..a0adbdb 100644 --- a/src/admin/web/manage.ts +++ b/src/admin/web/manage.ts @@ -17,6 +17,7 @@ import { } from "../../shared/users/schema"; import { getLastNImages } from "../../shared/file-storage/image-history"; import { blacklists, parseCidrs, whitelists } from "../../shared/cidr"; +import { invalidatePowHmacKey } from "../../user/web/pow-captcha"; const router = Router(); @@ -313,8 +314,10 @@ router.post("/maintenance", (req, res) => { const temps = users.filter((u) => u.type === "temporary"); temps.forEach((user) => { user.expiresAt = Date.now(); + user.disabledReason = "Admin forced expiration." userStore.upsertUser(user); }); + invalidatePowHmacKey() flash.type = "success"; flash.message = `${temps.length} temporary users marked for expiration.`; break; diff --git a/src/admin/web/views/admin_anti-abuse.ejs b/src/admin/web/views/admin_anti-abuse.ejs index 5efb9b1..954839c 100644 --- a/src/admin/web/views/admin_anti-abuse.ejs +++ b/src/admin/web/views/admin_anti-abuse.ejs @@ -12,13 +12,12 @@ } #token-manage { - width: 100%; display: flex; + width: 100%; } #token-manage button { - padding: 0.5em; - margin: 0 0.5em; flex-grow: 1; + margin: 0 0.5em; } diff --git a/src/admin/web/views/admin_create-user.ejs b/src/admin/web/views/admin_create-user.ejs index a46f7e3..23ec008 100644 --- a/src/admin/web/views/admin_create-user.ejs +++ b/src/admin/web/views/admin_create-user.ejs @@ -51,9 +51,8 @@
- Temporary users will be disabled after the specified duration, and their records will be deleted 72 hours after that. - These options apply only to new - temporary users; existing ones use whatever options were in effect when they were created. + Temporary users will be disabled after the specified duration, and their records will be permanently deleted after some time. + These options apply only to new temporary users; existing ones use whatever options were in effect when they were created.
diff --git a/src/admin/web/views/admin_index.ejs b/src/admin/web/views/admin_index.ejs index 61a10fd..339368f 100644 --- a/src/admin/web/views/admin_index.ejs +++ b/src/admin/web/views/admin_index.ejs @@ -32,7 +32,7 @@