Httpsignature verification failing
Closed this issue · 5 comments
I've started verifying incoming activities now in the Drupal module. However, most are failing. As a consequence, incoming messages (even from followers) are unpublished and not visible on the timeline.
This is probably due to mastodon/mastodon#14556 - I had to change the creation of the signature because of this change when sending out activities to my followers.
Tricky stuff :) I'll dig in deeper myself first to figure out what needs to change and run it in my environment for a few days once I got something.
So, I finally got signature verification running when the signature is in the headers, and with digest as well.
There's the case (as far as I can see for DELETE requests for removed users), where the value of a signature is in the payload, so that one isn't covered yet.
To get there, I added a dependency in my project on two libraries:
- liamdennehy/http-signatures-php : allows for signing and verifying
- nyholm/psr7 : helpers to convert Symfony request to Guzzle message
The verification looks like this:
(Note: ignoreSignatureExceptionMessages() returns an array of messages which I don't want to log at all - at least currently, those are e.g. 410 gone responses, could be more elegant of course - getServer() is a method in my class to instantiate the AP server object)
public function verifySignature(Request $request, string $actor) {
$verified = FALSE;
try {
$actor = $this->getServer()->actor($actor);
$keyId = $actor->get('publicKey')['id'];
$publicKeyPem = $actor->getPublicKeyPem();
$context = new Context([
'keys' => [
$keyId => $publicKeyPem,
]
]);
$psr17Factory = new Psr17Factory();
$psrHttpFactory = new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory);
$psrRequest = $psrHttpFactory->createRequest($request);
if ($context->verifier()->isSignedWithDigest($psrRequest)) {
$verified = TRUE;
}
else {
$verified = $context->verifier()->isSigned($psrRequest);
}
}
catch (\Exception $e) {
$store = TRUE;
$message = $e->getMessage();
foreach ($this->ignoreSignatureExceptionMessages() as $m) {
if (strpos($message, $m) !== FALSE) {
$store = FALSE;
break;
}
}
if ($store) {
$this->logger->error('Signature verifying exception: @message', ['@message' => $message]);
}
}
return $verified;
}
So, would you be interested to start depending on these libraries for this project? It would remove the burden of keeping up with the RFC and let that depend on a different project. If so, I can spent some time getting that in as it's not that much change actually :)
Note: there's also the option to just drop that verification class in this project too and document the libraries from my previous comment with an example. I could totally live with that as well!
Hi swentel,
Nice investigation. No problem to depend on these libraries, they seems to be well tested.
I saw your issue but had not much time to make some more investigation. Thanks for this work.
Could you make a pull request please ? I will review it with pleasure... As usual ;)
Ok cool! I'll figure out the outer variation first as well for signing and then create a pull request. Might take at least a week or so, but there's no rush :)
Closing this one. With the update of phpseclib I've removed the dependencies I used.
I'm going to run this code in production now and see what's going to happen and start refactoring the verify method in this package where needed (I still do the parsing of the incoming request myself because it fails by default).