Merge pull request #12 from whs-authz-authn-project/feature/csrf

데일리스크럼 코드리뷰에서 2차 확인까지 마쳐서 merge 진행
This commit is contained in:
김민곤 2025-06-02 00:21:20 +09:00 committed by GitHub
commit db242c4465
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5,18 +5,27 @@ import { HttpUtils } from "../utils/http";
const httpUtils = new HttpUtils(); const httpUtils = new HttpUtils();
export class CsrfCheck { export class CsrfCheck {
private isTargetUri(uri: string): boolean {
if (
uri.includes("client_id=") &&
(uri.includes("response_type=") ||
uri.includes("grant_type=") ||
uri.includes("redirect_uri=") ||
uri.includes("scope=") ||
uri.includes("state=") ||
uri.includes("nonce="))
) {
return true;
}
return false;
}
private isOauthUri(request: Request): boolean { private isOauthUri(request: Request): boolean {
const query = request.getQuery() || ""; const uri = request.getUrl() || "";
// Check if the request is an OAuth authorization request // Check if the request is an OAuth authorization request
if ( if (this.isTargetUri(uri)) {
query.includes("client_id=") &&
(query.includes("response_type=") ||
query.includes("grant_type=") ||
query.includes("redirect_uri=") ||
query.includes("scope=") ||
query.includes("state="))
) {
return true; return true;
} }
@ -25,23 +34,10 @@ export class CsrfCheck {
private isOauthRedirectResponse(response: Response): boolean { private isOauthRedirectResponse(response: Response): boolean {
const status = response.getCode(); const status = response.getCode();
const locationHeader = httpUtils.getHeaderValue( const uri =
response.getHeaders(), httpUtils.getHeaderValue(response.getHeaders(), "location") || "";
"location"
);
if ( if (status >= 300 && status < 400 && this.isTargetUri(uri)) {
status >= 300 &&
status < 400 &&
locationHeader &&
(locationHeader.includes("client_id=") ||
locationHeader.includes("response_type=") ||
locationHeader.includes("grant_type=") ||
locationHeader.includes("redirect_uri=") ||
locationHeader.includes("scope=") ||
locationHeader.includes("state=") ||
locationHeader.includes("code=")) // code is also common in OAuth redirects
) {
return true; return true;
} }
return false; return false;
@ -49,7 +45,9 @@ export class CsrfCheck {
private isStateInQuery(request: Request): boolean { private isStateInQuery(request: Request): boolean {
const query = request.getQuery(); const query = request.getQuery();
const stateValue = httpUtils.getQueryParam(query || "", "state"); const stateValue =
httpUtils.getQueryParam(query || "", "state") ||
httpUtils.getQueryParam(query || "", "nonce");
if (!stateValue) { if (!stateValue) {
return false; return false;
} }
@ -72,17 +70,18 @@ export class CsrfCheck {
// 요청에서 보낸 state 추출 // 요청에서 보낸 state 추출
const query = request.getQuery() || ""; const query = request.getQuery() || "";
const originalState = httpUtils.getQueryParam(query, "state"); const originalState =
httpUtils.getQueryParam(query, "state") ||
httpUtils.getQueryParam(query || "", "nonce");
// 리다이렉트 URL에서 쿼리 부분만 추출 // 리다이렉트 URL에서 쿼리 부분만 추출
const locationHeader = httpUtils.getHeaderValue( const locationHeader = httpUtils.getHeaderValue(
response.getHeaders(), response.getHeaders(),
"location" "location"
); );
const responseState = httpUtils.getQueryParamFromURI( const responseState =
locationHeader || "", httpUtils.getQueryParamFromURI(locationHeader || "", "state") ||
"state" httpUtils.getQueryParamFromURI(locationHeader || "", "nonce");
);
// state가 없거나, 요청값과 다르면 CSRF 위험 // state가 없거나, 요청값과 다르면 CSRF 위험
if (!responseState) { if (!responseState) {