mirror of
https://github.com/Wolvan/poll.horse.git
synced 2024-11-21 20:47:59 +01:00
Use CSRF token to discourage botting
A suggestion to avoid stupid bots to vote on polls was a token that gets checked to a session cookie on vote submission.
This commit is contained in:
parent
6a155f2eb4
commit
ab151cb732
3 changed files with 22 additions and 0 deletions
|
@ -36,6 +36,7 @@
|
||||||
<section class="notepad">
|
<section class="notepad">
|
||||||
<div class="notepad-border"></div>
|
<div class="notepad-border"></div>
|
||||||
<form action="{{ BACKEND_BASE_PATH }}/_backend/vote-form/{{ POLL_ID }}" method="POST">
|
<form action="{{ BACKEND_BASE_PATH }}/_backend/vote-form/{{ POLL_ID }}" method="POST">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ CSRF_TOKEN }}">
|
||||||
<section class="poll-title">
|
<section class="poll-title">
|
||||||
{{ POLL_TITLE }}
|
{{ POLL_TITLE }}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { CookieOptions, Router } from "express";
|
||||||
import { BackendPoll as Poll, DupeCheckMode } from "./Poll";
|
import { BackendPoll as Poll, DupeCheckMode } from "./Poll";
|
||||||
import { MAX_POLL_OPTIONS, MAX_CHARACTER_LENGTH } from "./Config";
|
import { MAX_POLL_OPTIONS, MAX_CHARACTER_LENGTH } from "./Config";
|
||||||
import Storage from "./Storage";
|
import Storage from "./Storage";
|
||||||
|
import crypto from "crypto";
|
||||||
|
|
||||||
function randomString(length = 10, charset = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789") {
|
function randomString(length = 10, charset = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789") {
|
||||||
let result = "";
|
let result = "";
|
||||||
|
@ -213,12 +214,25 @@ export default async function init(router: Router, polls: Storage): Promise<void
|
||||||
const id = req.params.id;
|
const id = req.params.id;
|
||||||
const votes = [].concat(req.body["poll-option"]);
|
const votes = [].concat(req.body["poll-option"]);
|
||||||
|
|
||||||
|
const csrfTokenFromForm = req.body["csrf_token"];
|
||||||
|
const csrfTokenFromCookie = req.cookies.csrftoken;
|
||||||
|
|
||||||
|
if (csrfTokenFromForm !== csrfTokenFromCookie) return res.redirect(`/${id}?error=${
|
||||||
|
encodeURIComponent("Invalid CSRF token")
|
||||||
|
}&options=${
|
||||||
|
encodeURIComponent(votes.slice(0, MAX_POLL_OPTIONS).join("\uFFFE"))
|
||||||
|
}`);
|
||||||
|
|
||||||
const error = await voteOnPoll(id, votes, {
|
const error = await voteOnPoll(id, votes, {
|
||||||
ip: req.headers["x-forwarded-for"] as string || req.socket.remoteAddress || "",
|
ip: req.headers["x-forwarded-for"] as string || req.socket.remoteAddress || "",
|
||||||
setCookie: res.cookie.bind(res),
|
setCookie: res.cookie.bind(res),
|
||||||
cookies: req.cookies
|
cookies: req.cookies
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res.cookie("csrftoken", crypto.randomBytes(32).toString("base64"), {
|
||||||
|
httpOnly: true,
|
||||||
|
});
|
||||||
|
|
||||||
if (!error) return res.redirect("/" + id + "/r");
|
if (!error) return res.redirect("/" + id + "/r");
|
||||||
if (error.statusCode === 404) return res.redirect("/");
|
if (error.statusCode === 404) return res.redirect("/");
|
||||||
res.redirect(`/${id}?error=${
|
res.redirect(`/${id}?error=${
|
||||||
|
|
|
@ -7,6 +7,7 @@ import fetch from 'node-fetch';
|
||||||
import { program } from "commander";
|
import { program } from "commander";
|
||||||
import { FrontendPoll as Poll, PollResult } from "./Poll";
|
import { FrontendPoll as Poll, PollResult } from "./Poll";
|
||||||
import { MAX_CHARACTER_LENGTH, MAX_POLL_OPTIONS } from "./Config";
|
import { MAX_CHARACTER_LENGTH, MAX_POLL_OPTIONS } from "./Config";
|
||||||
|
import crypto from "crypto";
|
||||||
|
|
||||||
const RenderBuffer = new WeakMap();
|
const RenderBuffer = new WeakMap();
|
||||||
const RenderReplacements = new WeakMap();
|
const RenderReplacements = new WeakMap();
|
||||||
|
@ -158,7 +159,13 @@ export default function init(router: Router): void {
|
||||||
</div>`
|
</div>`
|
||||||
).join("");
|
).join("");
|
||||||
|
|
||||||
|
const csrfToken = req.cookies.csrftoken || crypto.randomBytes(32).toString("base64");
|
||||||
|
res.cookie("csrftoken", csrfToken, {
|
||||||
|
httpOnly: true,
|
||||||
|
});
|
||||||
|
|
||||||
await displayPage(req, res, "poll.html", {
|
await displayPage(req, res, "poll.html", {
|
||||||
|
"CSRF_TOKEN": csrfToken,
|
||||||
"POLL_ID": poll.id,
|
"POLL_ID": poll.id,
|
||||||
"POLL_TITLE": xss(poll.title),
|
"POLL_TITLE": xss(poll.title),
|
||||||
"POLL_OPTION_DIVS": pollOptions,
|
"POLL_OPTION_DIVS": pollOptions,
|
||||||
|
|
Loading…
Reference in a new issue