diff --git a/packages/backend/src/controller/clientsecretCheck.ts b/packages/backend/src/controller/clientsecretCheck.ts new file mode 100644 index 0000000..8bb01b3 --- /dev/null +++ b/packages/backend/src/controller/clientsecretCheck.ts @@ -0,0 +1,33 @@ +import type { SDK } from "caido:plugin"; +import type { Request } from "caido:utils"; + +export class ClientSecretController { + test(req: Request): boolean { + const query = req.getQuery() ?? ""; /* URL에서 검사 */ + + const bodyRaw = req.getBody(); /* BODY 에서 검사 */ + const body = typeof bodyRaw === "string" ? bodyRaw : Array.isArray(bodyRaw) ? bodyRaw.join("&") : ""; + + const authRaw = req.getHeader("authorization"); /* authz 헤더 에서 검사 */ + const auth = typeof authRaw === "string" ? authRaw : Array.isArray(authRaw) ? authRaw.join(" ") : ""; + + return ( + query.includes("client_secret=") || + body.includes("client_secret=") || + auth.toLowerCase().startsWith("basic Y2xpZW50X3NlY3JldA") + ); + } + + async report(sdk: SDK, req: Request): Promise { + const url = req.getUrl(); + + await sdk.findings.create({ + title: "Exposed client_secret", + description: `The request to \`${url}\` contains a potential exposure of the OAuth2 \`client_secret\`.`, + request: req, + reporter: "Client_Secret_Finder", + dedupeKey: "client_secret_exposure" + }); + } +} + diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 0165988..1d17c25 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -6,7 +6,8 @@ import { CsrfCheck } from "./controller/csrfCheck"; import { PKCECheck } from "./controller/PKCECheck"; import { AccessTokenLeakController } from "./controller/accessTokenDetector"; import { ScopeDetection } from "./controller/scopeDetection"; -import { NonceCheckController } from "./controller/nonceCheck"; +// import { NonceCheckController } from "./controller/nonceCheck"; +import { ClientSecretController } from "./controller/clientsecretCheck"; export type API = DefineAPI<{}>; @@ -16,7 +17,8 @@ const csrfCheck = new CsrfCheck(); const pkceCheckController = new PKCECheck(); const tokenCheck = new AccessTokenLeakController(); const ScopeDetectionController = new ScopeDetection(); -const nonceCheckController = new NonceCheckController(); +// const nonceCheckController = new NonceCheckController(); +const clientSecretController = new ClientSecretController(); export function init(sdk: SDK) { sdk.events.onInterceptResponse(async (sdk, req: Request, res: Response) => { @@ -25,19 +27,26 @@ export function init(sdk: SDK) { await tokenCheck.testReq(sdk, req); await tokenCheck.testResp(sdk, res, req); await ScopeDetectionController.scan(sdk, req.getUrl()); + // await clientSecretController.report(sdk,req); - if (NonceCheckController.isOidcFlow(req, res)) { - await sdk.findings.create({ - title: "OIDC Flow Detected", - description: "The request appears to be part of an OIDC flow.", - request: req, - reporter: "", - }); - } + // if (NonceCheckController.isOidcFlow(req, res)) { + // await sdk.findings.create({ + // title: "OIDC Flow Detected", + // description: "The request appears to be part of an OIDC flow.", + // request: req, + // reporter: "", + // }); + // } }); - /* + sdk.events.onInterceptRequest(async (sdk, req: Request) => { + if (clientSecretController.test(req)) { + await clientSecretController.report(sdk,req); + } + });/* + + await clientSecretController.report(sdk,req);}) const result = authZCodeGrantController.testReq(req) || implicitGrantController.testReq(req);