From f0366ec5bae50ebd610d7d31009feb314d76c7c9 Mon Sep 17 00:00:00 2001 From: usbharu <64310155+usbharu@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:18:51 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20HTTP=20Signature=E3=81=A7=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E3=81=AA=E3=83=98=E3=83=83=E3=83=80=E3=83=BC=E3=81=8C?= =?UTF-8?q?=E5=90=AB=E3=81=BE=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E3=81=8B?= =?UTF-8?q?=E3=82=92=E6=A4=9C=E8=A8=BC=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/inbox/InboxJobProcessor.kt | 12 +++++++++ .../application/config/SecurityConfig.kt | 2 +- .../httpsignature/HttpSignatureFilter.kt | 19 +------------ .../HttpSignatureUserDetailsService.kt | 27 ++++++++++++++++--- 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt index 1e4aeb2d..d1bebb2b 100644 --- a/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt +++ b/src/main/kotlin/dev/usbharu/hideout/activitypub/service/inbox/InboxJobProcessor.kt @@ -14,6 +14,7 @@ import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.core.service.job.JobProcessor import dev.usbharu.hideout.util.RsaUtil import dev.usbharu.httpsignature.common.HttpHeaders +import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.common.PublicKey import dev.usbharu.httpsignature.verify.HttpSignatureVerifier @@ -34,6 +35,15 @@ class InboxJobProcessor( ) : JobProcessor { private suspend fun verifyHttpSignature(httpRequest: HttpRequest, signature: Signature): Boolean { + + val requiredHeaders = when (httpRequest.method) { + HttpMethod.GET -> getRequiredHeaders + HttpMethod.POST -> postRequiredHeaders + } + if (signature.headers.containsAll(requiredHeaders).not()) { + return false + } + val user = try { userQueryService.findByKeyId(signature.keyId) } catch (_: FailedToGetResourcesException) { @@ -96,5 +106,7 @@ class InboxJobProcessor( companion object { private val logger = LoggerFactory.getLogger(InboxJobProcessor::class.java) + private val postRequiredHeaders = listOf("(request-target)", "date", "host", "digest") + private val getRequiredHeaders = listOf("(request-target)", "date", "host") } } diff --git a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt index dece6e79..84df4aa3 100644 --- a/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt +++ b/src/main/kotlin/dev/usbharu/hideout/application/config/SecurityConfig.kt @@ -121,7 +121,7 @@ class SecurityConfig { userQueryService: UserQueryService ): HttpSignatureFilter { val httpSignatureFilter = - HttpSignatureFilter(DefaultSignatureHeaderParser(), transaction, apUserService, userQueryService) + HttpSignatureFilter(DefaultSignatureHeaderParser()) httpSignatureFilter.setAuthenticationManager(authenticationManager) httpSignatureFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false) val authenticationEntryPointFailureHandler = diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt index e814e568..d4332651 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureFilter.kt @@ -1,23 +1,15 @@ package dev.usbharu.hideout.core.infrastructure.springframework.httpsignature -import dev.usbharu.hideout.activitypub.service.objects.user.APUserService -import dev.usbharu.hideout.application.external.Transaction -import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException -import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.httpsignature.common.HttpHeaders import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.verify.SignatureHeaderParser import jakarta.servlet.http.HttpServletRequest -import kotlinx.coroutines.runBlocking import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter import java.net.URL class HttpSignatureFilter( - private val httpSignatureHeaderParser: SignatureHeaderParser, - private val transaction: Transaction, - private val apUserService: APUserService, - private val userQueryService: UserQueryService + private val httpSignatureHeaderParser: SignatureHeaderParser ) : AbstractPreAuthenticatedProcessingFilter() { override fun getPreAuthenticatedPrincipal(request: HttpServletRequest?): Any? { @@ -33,15 +25,6 @@ class HttpSignatureFilter( } catch (_: RuntimeException) { return "" } - runBlocking { - transaction.transaction { - try { - userQueryService.findByKeyId(signature.keyId) - } catch (_: FailedToGetResourcesException) { - apUserService.fetchPerson(signature.keyId) - } - } - } return signature.keyId } diff --git a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt index a2e2a258..3acc12f6 100644 --- a/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt +++ b/src/main/kotlin/dev/usbharu/hideout/core/infrastructure/springframework/httpsignature/HttpSignatureUserDetailsService.kt @@ -5,10 +5,12 @@ import dev.usbharu.hideout.core.domain.exception.FailedToGetResourcesException import dev.usbharu.hideout.core.domain.exception.HttpSignatureVerifyException import dev.usbharu.hideout.core.query.UserQueryService import dev.usbharu.hideout.util.RsaUtil +import dev.usbharu.httpsignature.common.HttpMethod import dev.usbharu.httpsignature.common.HttpRequest import dev.usbharu.httpsignature.common.PublicKey import dev.usbharu.httpsignature.verify.FailedVerification import dev.usbharu.httpsignature.verify.HttpSignatureVerifier +import dev.usbharu.httpsignature.verify.SignatureHeaderParser import kotlinx.coroutines.runBlocking import org.slf4j.LoggerFactory import org.springframework.security.authentication.BadCredentialsException @@ -20,14 +22,16 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA class HttpSignatureUserDetailsService( private val userQueryService: UserQueryService, private val httpSignatureVerifier: HttpSignatureVerifier, - private val transaction: Transaction + private val transaction: Transaction, + private val httpSignatureHeaderParser: SignatureHeaderParser ) : AuthenticationUserDetailsService { override fun loadUserDetails(token: PreAuthenticatedAuthenticationToken): UserDetails = runBlocking { if (token.principal !is String) { throw IllegalStateException("Token is not String") } - if (token.credentials !is HttpRequest) { + val credentials = token.credentials + if (credentials !is HttpRequest) { throw IllegalStateException("Credentials is not HttpRequest") } @@ -40,10 +44,25 @@ class HttpSignatureUserDetailsService( } } + val signature = httpSignatureHeaderParser.parse(credentials.headers) + + val requiredHeaders = when (credentials.method) { + HttpMethod.GET -> getRequiredHeaders + HttpMethod.POST -> postRequiredHeaders + } + if (signature.headers.containsAll(requiredHeaders).not()) { + logger.warn( + "FAILED Verify HTTP Signature. required headers: {} but actual: {}", + requiredHeaders, + signature.headers + ) + throw BadCredentialsException("HTTP Signature. required headers: $requiredHeaders") + } + @Suppress("TooGenericExceptionCaught") val verify = try { httpSignatureVerifier.verify( - token.credentials as HttpRequest, + credentials, PublicKey(RsaUtil.decodeRsaPublicKeyPem(findByKeyId.publicKey), keyId) ) } catch (e: RuntimeException) { @@ -67,5 +86,7 @@ class HttpSignatureUserDetailsService( companion object { private val logger = LoggerFactory.getLogger(HttpSignatureUserDetailsService::class.java) + private val postRequiredHeaders = listOf("(request-target)", "date", "host", "digest") + private val getRequiredHeaders = listOf("(request-target)", "date", "host") } }