feat: HTTP Signatureで必要なヘッダーが含まれているかを検証するように

This commit is contained in:
usbharu 2023-11-28 14:18:51 +09:00
parent cc046393d6
commit f0366ec5ba
4 changed files with 38 additions and 22 deletions

View File

@ -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<InboxJobParam, InboxJob> {
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")
}
}

View File

@ -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 =

View File

@ -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
}

View File

@ -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<PreAuthenticatedAuthenticationToken> {
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")
}
}