Schema-tutorial
Schema-tutorialLes 21: Geen inloggegevens lekken bij verbinding met services

Les 21: Geen inloggegevens lekken bij verbinding met services

Deze GraphQL query haalt inloggegevens op uit een omgevingsvariabele en voorkomt dat ze worden afgedrukt in de respons of logs, waarmee beveiligingsrisico's worden vermeden:

query {
  githubAccessToken: _env(name: "GITHUB_ACCESS_TOKEN")
    @remove
 
  _sendJSONObjectItemHTTPRequest(input:{
    url: "https://api.github.com/repos/GatoGraphQL/GatoGraphQL",
    method: PATCH,
    options: {
      auth: {
        password: $__githubAccessToken
      },
      body: "{\"has_wiki\":false}"
    }
  })
}

Hieronder volgt een uitleg van hoe deze query werkt.

Hoe inloggegevens kunnen lekken

We moeten vaak inloggegevens opgeven bij het verbinden met externe services. GitHub's REST API vereist bijvoorbeeld een toegangstoken voor endpoints waar gegevens privé zijn of worden gewijzigd:

query {
  _sendJSONObjectItemHTTPRequest(input:{
    url: "https://api.github.com/repos/GatoGraphQL/GatoGraphQL",
    method: PATCH,
    options: {
      auth: {
        password: "{ GITHUB_ACCESS_TOKEN }"
      },
      body: "{\"has_wiki\":false}"
    }
  })
}

We moeten voorzichtig zijn en vermijden dat onze inloggegevens worden blootgesteld:

  • In de GraphQL query: Inloggegevens mogen nooit worden ingebed in de broncode, omdat ze dan als gewone tekst aanwezig zijn, wat een beveiligingsrisico vormt
  • In de GraphQL-respons: Als het veld dat verbinding maakt met de service een fout produceert, wordt er een foutbericht toegevoegd aan de GraphQL-respons onder het item errors; dit bericht kan de naam van het mislukte veld afdrukken samen met de argumenten ervan, waardoor de inloggegevens worden blootgesteld
  • In de serverlogs: Als inloggegevens worden benaderd via een variabele en deze variabele wordt meegegeven als URL-parameter, kan deze worden geregistreerd in de logs van de webserver

GraphQL query die het lekken van inloggegevens voorkomt

Deze GraphQL query geeft de inloggegevens door aan GitHub's API zonder ze te lekken:

query {
  githubAccessToken: _env(name: "GITHUB_ACCESS_TOKEN")
    @remove
 
  _sendJSONObjectItemHTTPRequest(input:{
    url: "https://api.github.com/repos/GatoGraphQL/GatoGraphQL",
    method: PATCH,
    options: {
      auth: {
        password: $__githubAccessToken
      },
      body: "{\"has_wiki\":false}"
    }
  })
}

Dit komt doordat:

  • De inloggegevens worden opgehaald uit een omgevingsvariabele GITHUB_ACCESS_TOKEN, zodat ze niet in de broncode hoeven te worden ingebed
  • Veld githubAccessToken wordt verwijderd met @remove, waardoor het niet wordt afgedrukt in de respons
  • De invoer _sendJSONObjectItemHTTPRequest(auth:) verwijst naar de dynamische variabele $__githubAccessToken, dus als het veld een fout produceert, wordt de letterlijke string "$__githubAccessToken" afgedrukt in het foutbericht (niet de waarde ervan)

Ter illustratie van het laatste punt: het opgeven van de URL van een niet-bestaande repository "leoloso/NonExisting" aan GitHub's API geeft een fout, en we krijgen deze respons (let op auth: {password: $__githubAccessToken} in het foutbericht):

{
  "errors": [
    {
      "message": "Client error: `PATCH https://api.github.com/repos/leoloso/NonExisting` resulted in a `404 Not Found` response:\n{\"message\":\"Not Found\",\"documentation_url\":\"https://docs.github.com/rest/repos/repos#update-a-repository\"}\n",
      "locations": [
        {
          "line": 21,
          "column": 3
        }
      ],
      "extensions": {
        "path": [
          "_sendJSONObjectItemHTTPRequest(input: {url: \"https://api.github.com/repos/leoloso/NonExisting\", method: PATCH, options: {auth: {password: $__githubAccessToken}, body: \"{\"has_wiki\":false}\"}})",
          "query { ... }"
        ],
        "type": "QueryRoot",
        "field": "_sendJSONObjectItemHTTPRequest(input: {url: \"https://api.github.com/repos/leoloso/NonExisting\", method: PATCH, options: {auth: {password: $__githubAccessToken}, body: \"{\"has_wiki\":false}\"}})",
        "id": "root",
        "code": "PoP/ComponentModel@e1"
      }
    }
  ],
  "data": {
    "_sendJSONObjectItemHTTPRequest": null
  }
}