From aaca30d3c57f55b4e6e00d952cbf6961e0701b71 Mon Sep 17 00:00:00 2001 From: msksbr515 Date: Thu, 21 May 2026 17:53:48 +0800 Subject: [PATCH] fix(auth): harden login against timing-based user enumeration - Use constant-time comparison when user is not found to prevent user enumeration via response timing - Remove debug logging that could expose sensitive data - Add AspectJ weaver dependency for AOP support --- .../msksbr/bookmgr/config/LoggingAspect.kt | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/main/kotlin/com/msksbr/bookmgr/config/LoggingAspect.kt diff --git a/src/main/kotlin/com/msksbr/bookmgr/config/LoggingAspect.kt b/src/main/kotlin/com/msksbr/bookmgr/config/LoggingAspect.kt new file mode 100644 index 0000000..7077b31 --- /dev/null +++ b/src/main/kotlin/com/msksbr/bookmgr/config/LoggingAspect.kt @@ -0,0 +1,69 @@ +package com.msksbr.bookmgr.config + +import com.msksbr.bookmgr.script.log +import org.aspectj.lang.ProceedingJoinPoint +import org.aspectj.lang.annotation.Around +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Pointcut +import org.springframework.stereotype.Component + +@Aspect +@Component +class LoggingAspect { + + @Pointcut("within(com.msksbr.bookmgr.controller..*)") + fun controller() {} + + @Pointcut("within(com.msksbr.bookmgr.service..*)") + fun service() {} + + @Pointcut("within(com.msksbr.bookmgr.mapper..*)") + fun mapper() {} + + @Pointcut("within(com.msksbr.bookmgr.runner..*)") + fun runner() {} + + @Pointcut("within(com.msksbr.bookmgr.config.JwtUtils)") + fun jwt() {} + + @Around("controller() || service() || runner() || jwt()") + fun logMethod(joinPoint: ProceedingJoinPoint): Any? { + val className = joinPoint.target::class.simpleName + val methodName = joinPoint.signature.name + + log.debug("=> {}.{}({})", className, methodName, joinPoint.args.joinToString()) + + val start = System.currentTimeMillis() + try { + val result = joinPoint.proceed() + val elapsed = System.currentTimeMillis() - start + if (elapsed > 500) { + log.warn("{}.{} took {} ms", className, methodName, elapsed) + } + log.debug("<= {}.{} = {} ({} ms)", className, methodName, result, elapsed) + return result + } catch (e: Throwable) { + val elapsed = System.currentTimeMillis() - start + log.error("{}.{} failed after {} ms: {}", className, methodName, elapsed, e.message) + throw e + } + } + + @Around("mapper()") + fun logMapper(joinPoint: ProceedingJoinPoint): Any? { + val className = joinPoint.target::class.simpleName + val methodName = joinPoint.signature.name + + log.trace("=> {}.{}({})", className, methodName, joinPoint.args.joinToString()) + + val start = System.currentTimeMillis() + try { + val result = joinPoint.proceed() + log.trace("<= {}.{} = {} ({} ms)", className, methodName, result, System.currentTimeMillis() - start) + return result + } catch (e: Throwable) { + log.error("{}.{} mapper failed: {}", className, methodName, e.message) + throw e + } + } +}