check for cross-domain redirects that bounce from an allowed domain to a blocked domain

This commit is contained in:
Hazelnoot 2024-12-18 10:29:25 -05:00
parent 7f19f8c10b
commit 05ff9ae93f

View file

@ -104,7 +104,7 @@ export class Resolver {
} }
if (!this.utilityService.isFederationAllowedHost(host)) { if (!this.utilityService.isFederationAllowedHost(host)) {
throw new UnrecoverableError(`instance is blocked: ${value}`); throw new UnrecoverableError(`cannot fetch AP object ${value}: blocked instance ${host}`);
} }
if (this.config.signToActivityPubGet && !this.user) { if (this.config.signToActivityPubGet && !this.user) {
@ -123,16 +123,25 @@ export class Resolver {
throw new UnrecoverableError(`invalid AP object ${value}: does not have ActivityStreams context`); throw new UnrecoverableError(`invalid AP object ${value}: does not have ActivityStreams context`);
} }
// HttpRequestService / ApRequestService have already checked that // Since redirects are allowed, we cannot safely validate an anonymous object.
// `object.id` or `object.url` matches the URL used to fetch the // Reject any responses without an ID, as all other checks depend on that value.
// object after redirects; here we double-check that no redirects
// bounced between hosts
if (object.id == null) { if (object.id == null) {
throw new UnrecoverableError(`invalid AP object ${value}: missing id`); throw new UnrecoverableError(`invalid AP object ${value}: missing id`);
} }
if (this.utilityService.punyHostPSLDomain(object.id) !== this.utilityService.punyHostPSLDomain(value)) { // We allow some limited cross-domain redirects, which means the host may have changed during fetch.
throw new UnrecoverableError(`invalid AP object ${value}: id ${object.id} has different host`); // Additional checks are needed to validate the scope of cross-domain redirects.
const finalHost = this.utilityService.extractDbHost(object.id);
if (finalHost !== host) {
// Make sure the redirect stayed within the same authority.
if (this.utilityService.punyHostPSLDomain(object.id) !== this.utilityService.punyHostPSLDomain(value)) {
throw new UnrecoverableError(`invalid AP object ${value}: id ${object.id} has different host`);
}
// Check if the redirect bounce from [allowed domain] to [blocked domain].
if (!this.utilityService.isFederationAllowedHost(finalHost)) {
throw new UnrecoverableError(`cannot fetch AP object ${value}: redirected to blocked instance ${finalHost}`);
}
} }
return object; return object;