mirror of
https://iceshrimp.dev/iceshrimp/iceshrimp
synced 2024-11-27 14:28:08 +09:00
[backend] Refetch user keys when HTTP Signature validation fails
If a user has had a key rotation, and nobody on this server follows
that user, we will not receive the Update activity with the new key
Therefore, when we encounter key validation errors we should check
for an up-to-date key.
References (other implementations):
* [Mastodon](fc9ab61448/app/controllers/concerns/signature_verification.rb (L96)
)
* [Akkoma](https://akkoma.dev/AkkomaGang/http_signatures/src/branch/main/lib/http_signatures/http_signatures.ex#L46)
This commit is contained in:
parent
092462d3a9
commit
0c9c04f89d
@ -95,11 +95,25 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
|
||||
}
|
||||
|
||||
// HTTP-Signatureの検証
|
||||
const httpSignatureValidated = httpSignature.verifySignature(
|
||||
let httpSignatureValidated = httpSignature.verifySignature(
|
||||
signature,
|
||||
authUser.key.keyPem,
|
||||
);
|
||||
|
||||
// If signature validation failed, try refetching the actor
|
||||
if (!httpSignatureValidated) {
|
||||
authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user);
|
||||
|
||||
if (authUser.key == null) {
|
||||
return "skip: failed to re-resolve user publicKey";
|
||||
}
|
||||
|
||||
httpSignatureValidated = httpSignature.verifySignature(
|
||||
signature,
|
||||
authUser.key.keyPem,
|
||||
);
|
||||
}
|
||||
|
||||
// また、signatureのsignerは、activity.actorと一致する必要がある
|
||||
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
|
||||
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る
|
||||
|
@ -87,11 +87,25 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
|
||||
}
|
||||
|
||||
// HTTP-Signatureの検証
|
||||
const httpSignatureValidated = httpSignature.verifySignature(
|
||||
let httpSignatureValidated = httpSignature.verifySignature(
|
||||
signature,
|
||||
authUser.key.keyPem,
|
||||
);
|
||||
|
||||
// If signature validation failed, try refetching the actor
|
||||
if (!httpSignatureValidated) {
|
||||
authUser.key = await dbResolver.refetchPublicKeyForApId(authUser.user);
|
||||
|
||||
if (authUser.key == null) {
|
||||
return 403;
|
||||
}
|
||||
|
||||
httpSignatureValidated = httpSignature.verifySignature(
|
||||
signature,
|
||||
authUser.key.keyPem,
|
||||
);
|
||||
}
|
||||
|
||||
if (!httpSignatureValidated) {
|
||||
return 403;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import { Cache } from "@/misc/cache.js";
|
||||
import { uriPersonCache, userByIdCache } from "@/services/user-cache.js";
|
||||
import type { IObject } from "./type.js";
|
||||
import { getApId } from "./type.js";
|
||||
import { resolvePerson } from "./models/person.js";
|
||||
import { resolvePerson, updatePerson } from "./models/person.js";
|
||||
import {redisClient, subscriber} from "@/db/redis.js";
|
||||
|
||||
const publicKeyCache = new Cache<UserPublickey | null>("publicKey", 60 * 30);
|
||||
@ -152,7 +152,7 @@ export default class DbResolver {
|
||||
*/
|
||||
public async getAuthUserFromKeyId(keyId: string): Promise<{
|
||||
user: CacheableRemoteUser;
|
||||
key: UserPublickey;
|
||||
key: UserPublickey | null;
|
||||
} | null> {
|
||||
const key = await publicKeyCache.fetch(
|
||||
keyId,
|
||||
@ -204,6 +204,15 @@ export default class DbResolver {
|
||||
key,
|
||||
};
|
||||
}
|
||||
|
||||
public async refetchPublicKeyForApId(user: CacheableRemoteUser): Promise<UserPublickey | null> {
|
||||
await updatePerson(user.uri!, undefined, undefined, user);
|
||||
let key = await UserPublickeys.findOneBy({ userId: user.id });
|
||||
if (key != null) {
|
||||
await publicKeyByUserIdCache.set(user.id, key);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
subscriber.on("message", async (_, data) => {
|
||||
|
Loading…
Reference in New Issue
Block a user