I initially thought it was something like that.
IP - - [26/Apr/2017:07:26:51 -0700] "GET /form-A HTTP/1.1" 200 3471 "http://www.example.com/form-B" UserAgent
^ They went from form B to form A.
Then 15 mins later:-
IP - - [26/Apr/2017:07:41:15 -0700] "POST /form-B HTTP/1.1" 302 23 "http://www.example.com/form-B" UserAgent
^Post from form B.
Which I thought was iffy, like maybe they copied the html from form B, edited it for whatever means, then submitted from their version of it. Exactly the thing the token should prevent (and did, if that's what happened).
But if the IP had been some random far flung place (or the usual places these things come from), I would not hesitate to believe that's what happened and ban the IP.
Being local, that set off all kinds of paranoid ideas. But also brought doubt that is was an intended hack and perhaps my code is flawed, allowing an innocent user could "trip the alarm".
The idea about having more than one form open in different browser tabs does allow this to happen without any "funny business" going on. It makes sense that the last form viewed overwrites the sessions for the previous one, so if the previous one is then submitted, it fails the security check.
When I tested myself, that is exactly what happened, so I believe that is what the user did.
On another site, I do have a system like that. Any funny business is detected and Boof!, They get blocked automatically, but with a option for redemption if a legitimate explanation can be given.
But where there is the possibility for false positives, I don't want to be too harsh. It would seem a dis-service to my client if I treat their visitors badly and give a bad UX (their site, not mine).
In this case they would not be able contact me, as I am only contactable through one of the forms, which they would be locked out from. But the person they were trying to contact with the form does also have a phone number on their profile, so they had that as an alternative. It is also possible they could just get frustrated with being locked out of the form and give up. Though I don't see any attempt to access it again. That brings another possibility, that they don't even realise the message was not sent, since there was no error message, they currently just get dumped back on the homepage.
One of the main points of the form was not to. Otherwise I would save myself the trouble of making forms, have
mailto: links and everyone gets spammed.
I think for now I'll go with the quick fix of displaying a deliberately vague error message, so in future, at least they will know the submission did not go through.
Hopefully this was a freak occurrence of those perticular circumstances and will not be an everyday thing.
But in the long term I will try to dream up a way to improve the system and remove the possibility of this happening.