apollo-link-persisted-queries icon indicating copy to clipboard operation
apollo-link-persisted-queries copied to clipboard

How to precompute query hashes, store it, and use it to whitelist persisted queries.

Open joonhocho opened this issue 6 years ago • 17 comments

I want disallow arbitrary queries from client to prevent abusive queries. How do I extract and compute query hashes and save it as a query white list file and use load it on apollo server to whitelist them? I've tried apollo-cli apollo queries:extract, but its hashed value does not match the one created by apollo-link-persisted-queries.

  • [ ] has-reproduction
  • [x] feature
  • [x] blocking
  • [ ] good first issue

joonhocho avatar Oct 10 '18 14:10 joonhocho

We had the same problem while trying to implement persisted queries in our apps.

I made a reproduction repository to clearly show that the hash generated by apollo-link-persisted-queries is different from the one generated by apollo-tooling.

https://github.com/abumalick/persisted-queries-showcase

You can clearly see the hash generated by apollo client:extract in this file.

And you can see the hash generated by apollo-link-persisted-queries in the deployed app. You can look in the request details in devtools.

If you want more details, I explained everything in the README

Also I tried hard, exploring apollo-tooling source code and playing with generateHash config to try to generate hashes that match but I could not do it.

Help would be very appreciated.

abumalick avatar Mar 14 '19 13:03 abumalick

you can see this issue for more informations about this: https://github.com/apollographql/apollo-tooling/issues/1117

abumalick avatar Apr 26 '19 01:04 abumalick

isn't the point of automatic persisted queries to not use whitelist ? if i understand correctly you can use persistgraphql

Sceat avatar Apr 29 '19 04:04 Sceat

you can use persistgraphql

NB. persistgraphql is being deprecated in favour of apollo-link-persisted-queries and apollo-cli...

poshaughnessy avatar Jun 05 '19 14:06 poshaughnessy

@abumalick why use the apollo-link-persisted-queries at all? Writing your own link is the easiest thing to make from it all! I wrote my own apollo link to work with the vue-apollo without problems.

If you know how to:

  • generate a map from normalized query string to some unique identifier
  • map each query in your apollo link implementation to a normalized string

you are at home.
Just use the generated map in your apollo link on the client side and for reversing on the server side.

I want to do the same but I am stuck with seemingly trivial task: how, for God's sake, use this apollo client:extract tool to generate the normalized query string --> unique identifier map.

After that I have to face another challenge: find someway to generate matching normalized stringified query (to use in my simple apollo link implementation)

bartero avatar Aug 12 '19 18:08 bartero

Manipulating the hash on the client side with apollo-link-peristed-queries is pretty easy, you can use the generateHash arg.

Like you said, the real problem is making these hashes match the normalized queries generated by apollo client:extract, I tried hard and eventually stopped working on it.

You should read this answer from apollo team if you didn't already: https://github.com/apollographql/apollo-tooling/issues/1117#issuecomment-486653821

Personally I chose to not work on something that is not supported by the package maintainers, it will feel hacky and will probably not be stable. I stopped working on this at the moment.

abumalick avatar Aug 12 '19 18:08 abumalick

@abumalick Is then only the <step of normalized stringifying of a query> problematic to repeat in same way (actually: with same function) in the apollo link (which is then passed to the generateHash function) and in the command generating the map of queries ?

bartero avatar Aug 12 '19 19:08 bartero

@abumalick https://www.apollographql.com/docs/platform/operation-registry/

Note: Operation safelisting is an Apollo Platform feature that is only available to subscribers of the Apollo Team and Enterprise plans. Visit this guide to learn more about the Apollo Platform.

... and it seems more clear now. This is an already made feature but for paid plans only, unfortunately (!)

bartero avatar Aug 12 '19 19:08 bartero

@abumalick Anyway, thanks for your reply!

bartero avatar Aug 12 '19 21:08 bartero

You are welcome. Sorry that I cannot help more.

abumalick avatar Aug 15 '19 14:08 abumalick

I've forked apollo repo to implement this for workaround. (In case you are interested)

https://github.com/Cerberus/apollo-persisted-hash

The result: top half is chrome screen-shot on network tab and the another one is part of white-list file that generated by apollo client:extract ..

Screen Shot example

Cerberus avatar Aug 21 '19 20:08 Cerberus

Hello everyone, Hello @abumalick again, I have worked out a very simple solution with no hacks at all by writing a very small amount of code and only using the official libraries from the apollo ecosystem: graphql-tag, graphql and apollo-utilities.

I have a script which traverse directories where I keep the *.graphql files (with queries) on the client side. It produces a json mapping all the queries to consecutive integers. It does use exactly the same loader tool from graphql-tag, which webpack uses - what your webpack setup can work with so it can! :-)

That script in full readable/formatted form is just 56 lines of code and makes up ~80% of all code to make the whole scheme with frontend <--> backend safely work.

I may share code snippets for the interested.

bartero avatar Aug 22 '19 15:08 bartero

Yes, it is interesting.

Would you consider to opensource it ? If it is very simple to implement it may bring interest of the community.

abumalick avatar Aug 22 '19 15:08 abumalick

Yes, I will think how would it be feasible to make an universal lib out of it. Will let know

bartero avatar Aug 22 '19 15:08 bartero

@bartero I am in the same exact situation, I think it would be nice for the community to create a tool to extract queries and a simple link for the server to filter.

Sytten avatar Apr 02 '20 02:04 Sytten

I just use the hashing function from this library to create a whitelist, and return a 403 if you make a request with a different hash. If you make a request using a valid hash but provide a query that doesn't correspond to the hash you'll get a 400 provided sha does not match query, even if the query wasn't persisted yet.

import gql from 'graphql-tag';
import _ from 'lodash';
import { defaultGenerateHash } from 'apollo-link-persisted-queries';

const LOGIN = gql`
  mutation Login($email: String!, $password: String!) {
    login(email: $email, password: $password) {
      success
      error
      __typename
    }
  }
`;

export const queries = {
  LOGIN,
};

export const whitelist = _.mapValues(queries, defaultGenerateHash);

stefanvs avatar Jun 19 '20 09:06 stefanvs

Hello, Any updates about queries hashes ? I worked on persisted queries too, but i would not use deprecated lib persistgraphql to do that, even if it's works.

antonin87 avatar Nov 12 '20 11:11 antonin87