Public Source Viewer
비나래아카이브 개발자 포털
실제 서비스 구조를 살펴볼 수 있는 공개용 코드 뷰어입니다. 인증, 세션, 외부 연동, 토큰, 관리자 식별 등 보안상 민감한 구현은 파일 단위 또는 줄 단위로 검열됩니다.
src/middleware/csrf.middleware.ts
공개 가능
1
import { Application, Request, Response, NextFunction } from 'express';
2
import { createHmac, randomBytes, timingSafeEqual } from 'crypto';
3
4
// csurf(미유지보수) 대체 자체 구현.
5
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
6
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
7
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
8
9
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
10
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
11
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
12
}
13
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
14
}
15
16
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
17
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
18
}
19
20
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
21
const salt = randomBytes(8).toString('hex');
22
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
23
}
24
25
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
26
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
27
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
28
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
29
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
30
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
31
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
32
return mac.length === expected.length && timingSafeEqual(mac, expected);
33
}
34
35
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
36
return (req.body && req.body._csrf)
37
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
38
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
39
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
40
|| '';
41
}
42
43
function csrfError(): Error & { code: string } {
44
const err = new Error('CSRF 토큰이 유효하지 않습니다.') as Error & { code: string };
45
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
46
return err;
47
}
48
49
function isSameOriginRequest(req: Request): boolean {
50
const source = req.get('origin') || req.get('referer');
51
if (!source) return true;
52
try {
53
const url = new URL(source);
54
return url.host === req.get('host');
55
} catch {
56
return false;
57
}
58
}
59
60
export function setupCsrfMiddleware(app: Application): void {
61
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
62
app.use((req: Request, _res: Response, next: NextFunction) => {
63
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
64
next();
65
});
66
67
app.use((req: Request, res: Response, next: NextFunction) => {
68
const unsafeMethod = ['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method);
69
const isVoicepeakSyncRequest = unsafeMethod
70
&& req.path.startsWith('/hinana/voicepeak/archive');
71
72
if (isVoicepeakSyncRequest) {
73
return next();
74
}
75
76
if (unsafeMethod && !isSameOriginRequest(req)) {
77
return res.status(403).send('출처가 올바르지 않습니다.');
78
}
79
80
if (unsafeMethod && req.path === '/toggle-theme') {
81
return next();
82
}
83
84
if (req.method === 'GET') {
85
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
86
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
87
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
88
httpOnly: false,
89
sameSite: 'lax',
90
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
91
});
92
return next();
93
}
94
95
if (unsafeMethod) {
96
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
97
return next(csrfError());
98
}
99
return next();
100
}
101
102
return next();
103
});
104
105
app.use((req: Request, res: Response, next: NextFunction) => {
106
res.locals.theme = req.session.theme || 'light';
107
108
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
109
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
110
}
111
112
next();
113
});
114
}
115
116
export function csrfErrorHandler(err: any, req: Request, res: Response, next: NextFunction): any {
117
[SECURITY REDACTED] 민감한 설정/인증/토큰 관련 코드입니다.
118
return next(err);
119
}
120
121
console.warn('CSRF 검증 실패:', req.method, req.path, err.message);
122
123
if (req.path === '/chat-gpt' || req.path === '/share-discord' || req.path === '/api/exhibition/review' || req.path.startsWith('/hinana/voicepeak/jobs') || req.path.startsWith('/hinana/voicepeak/archive') || req.path.startsWith('/hinana/voicepeak/sync') || req.path.startsWith('/hinana/japanese-lounge/entries')) {
124
return res.status(403).json({ error: 'CSRF 토큰이 유효하지 않습니다. 페이지를 새로고침 해주세요.' });
125
}
126
127
return res.status(403).send('CSRF 검증 실패. 페이지를 새로고침 후 다시 시도해주세요.');
128
}
129