我目前正在开发一个测验应用程序。当出现问题时,用户有 10 秒的时间回答,否则他们不会获得该问题的分数。一旦计时器到了,我想自动转到下一个问题。我目前面临的问题是如何让 10 秒倒计时器“无法被客户端破解”。
我最初的想法是在客户端使用类似 setTimeout()
的方法持续 10 秒,一旦计时器完成,就要求服务器获取下一个问题。这样做的问题是,客户端计时器可能被黑客攻击/修改为运行时间超过 10 秒,从而可能让某些用户有超过 10 秒的时间来回答问题。
client <--- sends question --- server
|
start timer for 10 seconds (as this is client-side, it could easily be extended)
|
.
10 seconds later
.
V
client --- ask for next question / send answer ---> server
为了保持其不可破解,我想到将时间检查逻辑移至服务器端。这将涉及在每个连接用户的服务器端保留两个变量(A
和 B
),一个代表发送问题的时间,另一个代表发送问题的时间。给出了答案。客户端计时器仍然会运行,只是服务器端使用时间戳执行一些验证来检查时间戳 A 和 B 之间的差异是否超过 10 秒:
client <--- sends question --- server (send question at timestamp `A`)
|
start timer for 10 seconds (as this is client-side, it could easily be extended)
|
.
10 seconds later
.
V
client --- ask for next question / send answer ---> server (receive request at timestamp `B`)
|
+-----------------------------------------------------+
v
server logic:
duration = B - A
if(duration > 10 seconds) {
// allocated time exceeded
}
但是,我发现了一些潜在的缺陷。问题从服务器到达客户端所需的时间以及服务器发送问题(时间A
)到客户端计时器启动之间的时间不会被计算在内。即时的,并且取决于用户与服务器的 ping/连接。当客户询问下一个问题时,也存在类似的 ping 问题。而且,我担心如果客户端本来应该运行10秒的计时器稍微落后一点,那么也会导致服务器端检查失败。因此,检查持续时间是否超过 10 秒是不够的,并且需要一些额外的缓冲区。然而,我觉得随意将缓冲区硬编码为 1 或 2 秒之类的时间仍然可能会导致问题,并且感觉像是一种不太可靠的解决方法。
问题:我想知道我是否缺少一种不同的方法来保持客户端计时器不可破解且准确。我还想尝试避免使用 setTimeout()
或类似方法为服务器端的每个连接用户创建单独的计时器,因为许多用户可以在一个给定的时间点连接,并且有如此多的用户在服务器上排队的计时器让人感觉资源匮乏。我还想尝试将客户端和服务器之间来回发送的消息数量保持在最低限度。
最佳答案
Cookie 怎么样?
设置带有唯一标记的 cookie。将其到期时间设置为 now()+15 秒。在服务器端保存 token 和时间。让客户端计时器保持运行,并在 10 秒后自动提交。
当答案到来时,如果没有cookie...这肯定意味着答案是在延迟后发送的(并且计时器被黑客入侵)。
因此,now() + 10 秒的 cookie 过期时间 + 大约 5 秒的宽限期应该足以补偿 HTTP 延迟。
如果他们破解了计时器,则 cookie 应该已过期(并被删除)。如果他们还破解了 cookie 过期时间(!),无论如何, token 将用于检索发送的问题的日期时间,并且您将其与收到的答案的日期时间进行比较。
关于JavaScript 不可破解的倒计时器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68332531/