What's a good way to "protect" a public TRPC endpoint

I'm working on an app with a Google Maps API endpoint accessible from our frontend. My challenge is preventing external abuse of this endpoint. I've planned rate limiting, but I'm looking for more ways to keep this endpoint secure and within our app's scope. Any tips or experiences in safeguarding such an API? Your advice would be super helpful! Thanks!
19 Replies
iDarkLightning
iDarkLightningβ€’13mo ago
Use an API key?
Xaohs
XaohsOPβ€’13mo ago
Yeah but from the frontend that would not make much sense, no? I'd be using the key to the API in my frontend that will eventually call the maps api As in, the trpc route that calls the Google Maps endpoint would still be vulnerable as an API key shared through the frontend isn't secure at all and people could easily get that api key and construct calls to my trpc endpoint which then calls Google maps, my goal is to avoid people outside my application constructing these requests essentially making use of the maps endpoint through my app. Rate limiting will help, but it won't stop this behaviour completely.
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
JWT's Ratelimits
Xaohs
XaohsOPβ€’13mo ago
Yes JWT's would work, but this API endpoint is meant for all users, not just authenticated users, so long as they're from my frontend code and not just calling it via crud/postman etc. Do you mean, i'd create a short-lived token for each user visiting the website, which is stored in my DB, and whenever a user requests the API endpoint that would call the Google Maps API I check if this user has a correct token (and is indeed from my frontend code) ?
FleetAdmiralJakob πŸ—• πŸ—— πŸ—™
yes would be another layer of security
Xaohs
XaohsOPβ€’13mo ago
Hmm, yeah I think that might be a good option I've implemented a token authentication system to ensure that only legitimate frontend requests can access certain API endpoints. These are some of the things i've done: 1. useToken Hook: In _app.tsx, I've set up a useToken hook. This hook keeps a short-lived token in localStorage. The token is generated by calling an API endpoint that creates a JWT (JSON Web Token) with an expiration time of 30 minutes. 2. TRPC Configuration: In the TRPC client configuration, I've set the request header x-token to the token stored in localStorage. 3. Middleware for Token Validation: On the server side, I've created a middleware in TRPC that validates the x-token header in each request. It checks if the provided token is a valid JWT using jsonwebtoken's validate function:
export const verifyToken = (token: string): boolean => {
try {
verify(token, someSecret);
return true;
} catch (error) {
return false;
}
};

const validateTokenMiddleware = t.middleware(({ next, ctx }) => {
const token = ctx.req?.headers['x-token'];
if (typeof token === 'string') {
const isValid = verifyToken(token);
if (isValid) {
return next({
...
});
}
}
throw new TRPCError({ code: 'UNAUTHORIZED' });
});

export const verifyToken = (token: string): boolean => {
try {
verify(token, someSecret);
return true;
} catch (error) {
return false;
}
};

const validateTokenMiddleware = t.middleware(({ next, ctx }) => {
const token = ctx.req?.headers['x-token'];
if (typeof token === 'string') {
const isValid = verifyToken(token);
if (isValid) {
return next({
...
});
}
}
throw new TRPCError({ code: 'UNAUTHORIZED' });
});

I believe this setup effectively secures my API endpoints by validating that users are indeed coming from my frontend. However, I'd love some suggestions or input on this setup. If you have any ideas or feedback, I'd love to hear them! Thanks! πŸ™Œ I guess technically people would still be able to just call my token creation procedure, get the token created, and use that token in the API calls.. but at least this is another layer.. not really sure what else I can do
ethan
ethanβ€’13mo ago
server actions/server components
NoobSlayer
NoobSlayerβ€’10mo ago
A little late to this but why not just add a cors policy
erik.gh
erik.ghβ€’10mo ago
not all requests come from a browser ;)
NoobSlayer
NoobSlayerβ€’10mo ago
op said that the endpoint should only be accessed from his frontend and not postman etc.
erik.gh
erik.ghβ€’10mo ago
yes but op also didnβ€˜t really solve the problem
NoobSlayer
NoobSlayerβ€’10mo ago
bruh that's why i answered cors is mostly the solution
erik.gh
erik.ghβ€’10mo ago
cors can be a solution to block foreign "frontends" from accessing your endpoint, sure, but what I am saying is that it does not solve the broader problem of abuse of op's api. using op's endpoint would be as easy as making the request on a server - literally just proxying. so this statement is still true and it's not even that difficult in my opinion the only options are either taking the risk of abuse or putting the endpoint behind auth but just "tightening the system" isn't going to cut it
erik.gh
erik.ghβ€’10mo ago
i don't think there is really a way to bypass the problem just because of how browsers work. you modify the token in the frontend? let me take a quick look at where you're generating the token and replicate the behaviour. you generate the modified token server side? alright let me hit the same endpoint and request one. btw on obscurity concepts like these: https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle
Kerckhoffs's principle
Kerckhoffs's principle (also called Kerckhoffs's desideratum, assumption, axiom, doctrine or law) of cryptography was stated by Dutch-born cryptographer Auguste Kerckhoffs in the 19th century. The principle holds that a cryptosystem should be secure, even if everything about the system, except the key, is public knowledge. This concept is widely...
Xaohs
XaohsOPβ€’10mo ago
Damn y'all came back to this huh. So the system that's currently in place for me is. I have an API endpoint setup that when called does some preliminary checks of checking the referer, the origin, user agent. This then generates a short lived 5 minute signed token and returns it. On the frontend side of things, this token is then saved. Everytime the expiry time comes close a new one is generated for the user. On the API endpoint in question (google maps in this case) I do a check if this token is correct and not expired, and if it's correct I let the request through. This is still open for abuse, an abuser could easily throw a request to my token endpoint, get a token, use that token to then make a new request to my google map endpoint for 5 minutes before fetching the other API endpoint for a new token again. Of course the abuser would have to know a token is required, which i'm not sure how they will find out as I don't leak any info, but yeah I am still unsure how other people do this without auth, as it's essential that this API remains public. Making people login with OAuth instantly fixes this issue of course. @erik.gh @Deveroonie , if you have any further insights i'd love to know. Our google maps endpoint is limited to a certain budget anyway, so if an abuser would attempt, they wouldn't get that far they'd only waste us a few dollars. Been running for more than a month now and no abuse has happened yet, but this could easily change.
erik.gh
erik.ghβ€’10mo ago
the abuser knows that a token is required, how to use the token and where to get that token from by just taking a look at the network tab this is the important part that really secures your situation. i am happy to hear that you haven’t hat any incidents yet, not sure if that’s because of your token system tho obfuscate what? what do you want to obfuscate to bypass the issue? i am literally saying that if you obfuscate the token in the frontend it's as easy as taking a look at the devtools to find out with what you're obfuscating it.
Xaohs
XaohsOPβ€’10mo ago
Yeah, it honestly sounds like the only actual good solution is putting it behind an auth wall Which isn't an option in this case for us
erik.gh
erik.ghβ€’10mo ago
yes i understand that i'd probably just try and reinforce the things i can rely on and have control over like rate limiting and setting a budget limit.
Xaohs
XaohsOPβ€’10mo ago
Yeah we have a extra strict rate limit on that api endpoint in specific and our budget is setup in a good way.
Want results from more Discord servers?
Add your server