简体   繁体   English

在 Scala Play Framework 中,是否有一种简单的方法来验证表单请求上的签名

[英]In Scala Play Framework, is there a simple way to verify signatures on form requests

I am trying to write a Scala Play Framework action that will verify a HmacSHA256 signature on an incoming POST request containing form-url-encoded data.我正在尝试编写一个 Scala Play Framework 操作,该操作将在包含表单 url 编码数据的传入 POST 请求上验证 HmacSHA256 签名。

This does not seem straightforward in the Play framework because: i) actions builders only have access to headers, but do not have access to the request body, and ii) in order to calculate the signature we have to treat the request body as Array[ByteString] , but when we come to process the form data we have to treat it as Map[String, Seq[String]] , the problem being thatPlay forces us to choose a single type for our request, and we cannot easily "cast" the request body to a different type.这在 Play 框架中看起来并不简单,因为:i) 动作构建器只能访问标头,但不能访问请求主体,以及 ii) 为了计算签名,我们必须将请求主体视为Array[ByteString] ,但是当我们开始处理表单数据时,我们必须将其视为Map[String, Seq[String]] ,问题是 Play 迫使我们为请求选择单一类型,我们不能轻易地“投射”请求主体为不同的类型。

The only solution I have been able to come up with is to use an ActionRefiner that returns a WrappedRequest that embeds a callback to validate the signature.我能够想出的唯一解决方案是使用ActionRefiner ,它返回一个嵌入回调的WrappedRequest来验证签名。 The callback in turn reparses the data using FormUrlEncodedParser.parse(new String(request.body.toArray)) .回调依次使用FormUrlEncodedParser.parse(new String(request.body.toArray))解析数据。 This approach is illustrated in the code below.下面的代码说明了这种方法。

This all seems overly convoluted.这一切似乎过于复杂。 Is there a simpler way to verify Hmac signatures in Play, or am I simply running up against limitations of the API?有没有更简单的方法来验证 Play 中的 Hmac 签名,或者我只是遇到了 API 的限制?

package actions

import akka.util.ByteString
import com.google.inject.Inject
import play.api.Logging
import play.api.mvc.Results.Unauthorized
import play.api.mvc._
import play.core.parsers.FormUrlEncodedParser
import services.SlackSignatureVerifierService

import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try

class SlackRequest[A](
    val validateSignature: String => Try[String],
    request: Request[A]
) extends WrappedRequest[A](request)

object SlackSignatureVerifyAction {

  implicit class SlackRequestByteStringValidator(
      slackRequest: SlackRequest[ByteString]
  ) {
    def validateSignatureAgainstBody(): Try[Map[String, Seq[String]]] = {
      val raw = slackRequest.body.utf8String
      slackRequest.validateSignature(raw) map { _ =>
        FormUrlEncodedParser.parse(new String(slackRequest.body.toArray))
      }
    }
  }

  val HEADERS_TIMESTAMP: String = "X-Slack-Request-Timestamp"
  val HEADERS_SIGNATURE: String = "X-Slack-Signature"
}

class SlackSignatureVerifyAction @Inject() (
    val parser: BodyParsers.Default,
    slackSignatureVerifierService: SlackSignatureVerifierService
)(implicit ec: ExecutionContext)
    extends ActionBuilder[SlackRequest, AnyContent]
    with ActionRefiner[Request, SlackRequest]
    with Logging {

  override protected def executionContext: ExecutionContext = ec

  override protected def refine[A](
      request: Request[A]
  ): Future[Either[Result, SlackRequest[A]]] = {

    val timestamp =
      request.headers.get(SlackSignatureVerifyAction.HEADERS_TIMESTAMP)

    val signature =
      request.headers.get(SlackSignatureVerifyAction.HEADERS_SIGNATURE)

    (timestamp, signature) match {
      case (Some(timestamp), Some(signature)) =>
        Future.successful {
          val validate = (body: String) =>
            slackSignatureVerifierService.validate(timestamp, body, signature)
          Right(new SlackRequest[A](validate, request))
        }
      case _ =>
        Future { Left(Unauthorized("Invalid signature headers")) }
    }

  }

}

You are right, there isn't an easy way to verify Hmac signatures on Play!你是对的,没有一种简单的方法可以在 Play 上验证 Hmac 签名! projects.项目。 In the end, your approach seems to have a very reasonable implementation and could be easier adapted to other providers, such as GitHub and Stripe, that uses Hmac signatures.最后,您的方法似乎有一个非常合理的实现,并且可以更容易地适应其他使用 Hmac 签名的提供商,例如 GitHub 和 Stripe。

I really think it could be a good open-source project to provide an Action with a wrapped Request or even a Service with a method to do custom signature validation for other providers.我真的认为它可能是一个很好的开源项目,它可以提供一个带有包装RequestAction ,甚至是一个带有为其他提供者进行自定义签名验证的方法的Service Why don't you help the Play community with an open-source project over GitHub?为什么不通过 GitHub 的开源项目帮助 Play 社区?

I have created a new Play module to validate Hmac signatures.我创建了一个新的 Play 模块来验证 Hmac 签名。 Details can be found here:详细信息可以在这里找到:

https://github.com/phelps-sg/play-hmac-signatures https://github.com/phelps-sg/play-hmac-signatures

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM