Skip to content

Commit ad6e4d2

Browse files
committed
feat: prevent public_key endpoint from being cached #1234
1 parent e5928be commit ad6e4d2

File tree

7 files changed

+81
-2
lines changed

7 files changed

+81
-2
lines changed

api/crypto/crypto.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,30 @@ package crypto
22

33
import (
44
"net/http"
5+
"time"
56

67
"github.com/0xJacky/Nginx-UI/internal/crypto"
78
"github.com/gin-gonic/gin"
9+
"github.com/google/uuid"
810
"github.com/uozi-tech/cosy"
911
)
1012

1113
// GetPublicKey generates a new ED25519 key pair and registers it in the cache
1214
func GetPublicKey(c *gin.Context) {
15+
var data struct {
16+
Timestamp int64 `json:"timestamp" binding:"required"`
17+
Fingerprint string `json:"fingerprint" binding:"required"`
18+
}
19+
20+
if !cosy.BindAndValid(c, &data) {
21+
return
22+
}
23+
24+
if time.Now().Unix()-data.Timestamp > 10 {
25+
cosy.ErrHandler(c, crypto.ErrTimeout)
26+
return
27+
}
28+
1329
params, err := crypto.GetCryptoParams()
1430
if err != nil {
1531
cosy.ErrHandler(c, err)
@@ -18,5 +34,6 @@ func GetPublicKey(c *gin.Context) {
1834

1935
c.JSON(http.StatusOK, gin.H{
2036
"public_key": params.PublicKey,
37+
"request_id": uuid.NewString(),
2138
})
2239
}

api/crypto/router.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import "github.com/gin-gonic/gin"
55
func InitPublicRouter(r *gin.RouterGroup) {
66
g := r.Group("/crypto")
77
{
8-
g.GET("public_key", GetPublicKey)
8+
g.POST("public_key", GetPublicKey)
99
}
1010
}

app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"dependencies": {
1616
"@0xjacky/vue-github-button": "^3.1.1",
1717
"@ant-design/icons-vue": "^7.0.1",
18+
"@fingerprintjs/fingerprintjs": "^4.6.2",
1819
"@formkit/auto-animate": "^0.8.2",
1920
"@simplewebauthn/browser": "^13.1.2",
2021
"@uozi-admin/curd": "^4.5.3",

app/pnpm-lock.yaml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/src/lib/helper/fingerprint.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import FingerprintJS from '@fingerprintjs/fingerprintjs'
2+
3+
let fpPromise: Promise<string> | null = null
4+
5+
/**
6+
* Get browser fingerprint
7+
* Use caching mechanism to avoid duplicate calculations
8+
*/
9+
export async function getBrowserFingerprint(): Promise<string> {
10+
if (!fpPromise) {
11+
fpPromise = generateFingerprint()
12+
}
13+
return fpPromise
14+
}
15+
16+
/**
17+
* Generate browser fingerprint
18+
*/
19+
async function generateFingerprint(): Promise<string> {
20+
try {
21+
// Initialize FingerprintJS
22+
const fp = await FingerprintJS.load()
23+
24+
// Get fingerprint result
25+
const result = await fp.get()
26+
27+
// Return fingerprint ID
28+
return result.visitorId
29+
}
30+
catch (error) {
31+
console.warn('Failed to generate browser fingerprint, fallback to User Agent:', error)
32+
// If fingerprint generation fails, fallback to User Agent
33+
return navigator.userAgent
34+
}
35+
}
36+
37+
/**
38+
* Clear fingerprint cache
39+
* Force regenerate fingerprint
40+
*/
41+
export function clearFingerprintCache(): void {
42+
fpPromise = null
43+
}

app/src/lib/helper/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,5 @@ export {
7373
fromNow,
7474
urlJoin,
7575
}
76+
77+
export { clearFingerprintCache, getBrowserFingerprint } from './fingerprint'

app/src/lib/http/interceptors.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { CosyError } from './types'
22
import { http, useAxios } from '@uozi-admin/request'
3+
import dayjs from 'dayjs'
34
import JSEncrypt from 'jsencrypt'
45
import { storeToRefs } from 'pinia'
56
import use2FAModal from '@/components/TwoFA/use2FAModal'
7+
import { getBrowserFingerprint } from '@/lib/helper'
68
import { useNProgress } from '@/lib/nprogress/nprogress'
79
import { useSettingsStore, useUserStore } from '@/pinia'
810
import router from '@/routes'
@@ -16,7 +18,11 @@ const dedupe = useMessageDedupe()
1618
// Helper function for encrypting JSON data
1719
// eslint-disable-next-line ts/no-explicit-any
1820
async function encryptJsonData(data: any): Promise<string> {
19-
const cryptoParams = await http.get('/crypto/public_key')
21+
const fingerprint = await getBrowserFingerprint()
22+
const cryptoParams = await http.post('/crypto/public_key', {
23+
timestamp: dayjs().unix(),
24+
fingerprint,
25+
})
2026
const { public_key } = await cryptoParams
2127

2228
// Encrypt data with RSA public key

0 commit comments

Comments
 (0)