Skip to content

Commit 4442b90

Browse files
committed
Avoid form spam for the registration form.
Uses the same mechanism as vibenews for post spam.
1 parent 14a75a5 commit 4442b90

File tree

2 files changed

+23
-3
lines changed

2 files changed

+23
-3
lines changed

source/userman/web.d

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,24 @@ class UserManWebInterface {
262262
SessionVar!(string, "userFullName") m_sessUserFullName;
263263
SessionVar!(string, "userID") m_sessUserID;
264264
UserManAPISettings m_settings;
265+
size_t m_postEpoch;
265266
}
266267

267268
this(UserManAPI api, string prefix = "/")
268269
{
270+
import core.time : hours;
271+
import std.random : unpredictableSeed;
272+
import vibe.core.core : setTimer;
273+
269274
m_api = api;
270275
m_settings = api.settings;
271276
m_prefix = prefix;
277+
278+
// Invalidates pending forms every 2 to 4 hours, just making sure it
279+
// always starts with a random number, no need to be cryptographically
280+
// secure, this is just to make it a little more difficult for spammers
281+
m_postEpoch = unpredictableSeed();
282+
setTimer(2.hours, { m_postEpoch++; }, true);
272283
}
273284

274285
deprecated this(UserManController controller, string prefix = "/")
@@ -319,12 +330,15 @@ class UserManWebInterface {
319330
{
320331
string error = _error;
321332
auto settings = m_settings;
322-
render!("userman.register.dt", error, settings);
333+
auto postEpoch = m_postEpoch;
334+
render!("userman.register.dt", error, settings, postEpoch);
323335
}
324336

325337
@noAuth @errorDisplay!getRegister
326-
void postRegister(ValidEmail email, Nullable!string name, string fullName, ValidPassword password, Confirm!"password" passwordConfirmation)
338+
void postRegister(ValidEmail email, Nullable!string name, string fullName, ValidPassword password, Confirm!"password" passwordConfirmation, string check)
327339
{
340+
import std.conv : to;
341+
328342
string username;
329343
if (m_settings.useUserNames) {
330344
enforce(!name.isNull, "Missing user name field.");
@@ -335,6 +349,9 @@ class UserManWebInterface {
335349
username = name.get;
336350
} else username = email;
337351

352+
if (check != "a3fb"~m_postEpoch.to!string && check != "a3fb"~(m_postEpoch-1).to!string)
353+
throw new Exception("Form expired");
354+
338355
m_api.users.register(email, username, fullName, password);
339356

340357
if (m_settings.requireActivation) {

views/userman.register.dt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,7 @@ block userman.content
3535
label(for="passwordConfirmation") Password confirmation:
3636
td
3737
input(type="password", name="passwordConfirmation")
38-
button(type="submit") Register account
38+
input#fc(type="hidden", name="check", value="3fb")
39+
button(type="submit") Register account
40+
41+
script var fc=document.getElementById("fc");fc.setAttribute("value","a"+fc.getAttribute("value")+"#{postEpoch}");

0 commit comments

Comments
 (0)