feat(api): Add safeguard for incorrectly configured proxies

This commit is contained in:
Jesse Wierzbinski 2024-05-17 09:38:38 -10:00
parent 7a591a024e
commit c28628ebb3
No known key found for this signature in database
4 changed files with 58 additions and 4 deletions

View file

@ -11,6 +11,7 @@ import { bait } from "~middlewares/bait";
import { boundaryCheck } from "~middlewares/boundary-check";
import { ipBans } from "~middlewares/ip-bans";
import { logger } from "~middlewares/logger";
import { urlCheck } from "~middlewares/url-check";
import { Note } from "~packages/database-interface/note";
import { handleGlitchRequest } from "~packages/glitch-server/main";
import { routes } from "~routes";
@ -117,6 +118,7 @@ app.use(agentBans);
app.use(bait);
app.use(logger);
app.use(boundaryCheck);
app.use(urlCheck);
// Inject own filesystem router
for (const [route, path] of Object.entries(routes)) {

17
middlewares/url-check.ts Normal file
View file

@ -0,0 +1,17 @@
import { errorResponse } from "@response";
import { createMiddleware } from "hono/factory";
import { config } from "~packages/config-manager";
export const urlCheck = createMiddleware(async (context, next) => {
// Check that request URL matches base_url
const baseUrl = new URL(config.http.base_url);
if (new URL(context.req.url).origin !== baseUrl.origin) {
return errorResponse(
`Request URL ${context.req.url} does not match base URL ${baseUrl.origin}`,
400,
);
}
await next();
});

View file

@ -1,7 +1,11 @@
import { applyConfig, handleZodError } from "@api";
import { zValidator } from "@hono/zod-validator";
import { dualLogger } from "@loggers";
import { EntityValidator, SignatureValidator } from "@lysand-org/federation";
import {
EntityValidator,
type HttpVerb,
SignatureValidator,
} from "@lysand-org/federation";
import { errorResponse, jsonResponse, response } from "@response";
import type { SocketAddress } from "bun";
import { eq } from "drizzle-orm";
@ -53,6 +57,8 @@ export default (app: Hono) =>
async (context) => {
const { uuid } = context.req.valid("param");
const { signature, date } = context.req.valid("header");
const body: typeof EntityValidator.$Entity =
await context.req.valid("json");
const user = await User.fromId(uuid);
@ -105,7 +111,13 @@ export default (app: Hono) =>
);
const isValid = await validator
.validate(context.req.raw)
.validate(
signature,
new Date(Date.parse(date)),
context.req.method as HttpVerb,
new URL(context.req.url),
await context.req.text(),
)
.catch((e) => {
dualLogger.logError(
LogLevel.ERROR,
@ -121,8 +133,6 @@ export default (app: Hono) =>
}
const validator = new EntityValidator();
const body: typeof EntityValidator.$Entity =
await context.req.valid("json");
try {
// Add sent data to database

View file

@ -35,4 +35,29 @@ describe("API Tests", () => {
expect(data.error).toBeString();
expect(data.error).toContain("https://stackoverflow.com");
});
test("try sending a request with a different origin", async () => {
if (new URL(config.http.base_url).protocol === "http:") {
return;
}
const response = await sendTestRequest(
new Request(
new URL(
"/api/v1/instance",
base_url.replace("https://", "http://"),
),
{
method: "GET",
headers: {
Authorization: `Bearer ${tokens[0].accessToken}`,
},
},
),
);
expect(response.status).toBe(400);
const data = await response.json();
expect(data.error).toContain("does not match base URL");
});
});