There are path traversals in REST API source in the docs

Hello,

I’ve been following Lift-off II: Resolvers course on Odyssey and I noticed that the way that REST API requests are made is insecure and it can lead to developers making mistakes that result in security vulnerabilities in their systems.

What is the potential bug?

resolvers.js

const resolvers = {
  Query: {
    // returns an array of Tracks that will be used to populate the homepage grid of our web client
    tracksForHome: (_, __, { dataSources }) => {
      return dataSources.trackAPI.getTracksForHome();
    },
  },
  Track: {
    author: ({ authorId }, _, { dataSources }) => {
      return dataSources.trackAPI.getAuthor(authorId);
    },
  },
};

module.exports = resolvers;

track-api.js

const { RESTDataSource } = require('apollo-datasource-rest');

class TrackAPI extends RESTDataSource {
  constructor() {
    super();
    // the Catstronauts catalog is hosted on this server
    this.baseURL = 'https://odyssey-lift-off-rest-api.herokuapp.com/';
  }

  getTracksForHome() {
    return this.get('tracks');
  }

  getAuthor(authorId) {
    return this.get(`author/${authorId}`);
  }
}

module.exports = TrackAPI;

When we can see that the authorId parameter comes directly from the user and is not URL-encoded. Thus, the user can pass an authorId like 1/../../anotherresouce/2. Then, the application will create this URL:
https://odyssey-lift-off-rest-api.herokuapp.com/author/1/../../anotherresouce/2
which after normalization will become
https://odyssey-lift-off-rest-api.herokuapp.com/anotherresouce/2
It becomes a problem when not every user should be able to access anotherresouce/2 and the authentication part is done on the JavaScript layer, not on the REST API. This bug is called secondary context path traversal and here’s an example of such bug in Starbucks (not in GQL context): Hacking Starbucks and Accessing Nearly 100 Million Customer Records | Sam Curry

When chained with open redirects, it can also lead to SSRFs.

How to fix it?

Obviously, the vulnerability in the example application is not exposing any sensitive data and it’s every developer’s responsibility to make their applications secure. With that said, I think we should do our best to show developers the secure way to create their applications.

I think the Odyssey docs should follow the way that HTTP requests are done in Data sources - Apollo GraphQL Docs
which is using encodeURIComponent(id). This is sufficient to prevent this vulnerability.

class MoviesAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'https://movies-api.example.com/';
  }

  // GET
  async getMovie(id) {
    return this.get(
      `movies/${encodeURIComponent(id)}` // path
    );
  }
}
1 Like

Hello @gregxsunday! Thanks so much for your detailed writeup, we appreciate the time you took to think about this topic and provide a suggested solution!

We’ve gone ahead and added a bonus section in the lesson, under the title “Learn more: Securing your application using encodeURIComponent “. It’s a helpful addition for those learners eager for more info about security practices, but doesn’t distract from the main concepts of the lesson.

Thanks again for pointing this out!