Samouczek schematu
Samouczek schematuLekcja 21: Nie ujawniaj danych logowania przy łączeniu z usługami

Lekcja 21: Nie ujawniaj danych logowania przy łączeniu z usługami

To query GraphQL pobiera dane logowania ze zmiennej środowiskowej i unika ich wydrukowania w odpowiedzi lub logach, zapobiegając tym samym zagrożeniom bezpieczeństwa:

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}"
    }
  })
}

Poniżej znajduje się wyjaśnienie działania tego query.

Jak mogą wyciekać dane logowania

Często musimy podawać dane logowania przy łączeniu się z zewnętrznymi usługami. Na przykład REST API GitHuba wymaga tokenu dostępu dla endpointów, gdzie dane są prywatne lub są modyfikowane:

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

Musimy zachować ostrożność i unikać ujawniania naszych danych logowania:

  • W query GraphQL: Dane logowania nigdy nie mogą być osadzone w kodzie źródłowym, ponieważ byłyby w postaci zwykłego tekstu, co stanowiłoby zagrożenie bezpieczeństwa
  • W odpowiedzi GraphQL: Jeśli pole łączące się z usługą zwróci błąd, do odpowiedzi GraphQL zostanie dodany komunikat o błędzie pod wpisem errors; komunikat ten może zawierać nazwę pola, które się nie powiodło, wraz z jego argumentami, ujawniając tym samym dane logowania
  • W logach serwera: Jeśli dane logowania są dostępne przez zmienną, a ta zmienna jest przekazywana jako parametr URL, może zostać zapisana w logach serwera WWW

Query GraphQL zapobiegające wyciekaniu danych logowania

To query GraphQL przekazuje dane logowania do API GitHuba, unikając jednocześnie ich ujawnienia:

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}"
    }
  })
}

Dzieje się tak dlatego, że:

  • Dane logowania są pobierane ze zmiennej środowiskowej GITHUB_ACCESS_TOKEN, więc nie muszą być osadzone w kodzie źródłowym
  • Pole githubAccessToken jest usuwane przez @remove, więc nie jest drukowane w odpowiedzi
  • Wejście _sendJSONObjectItemHTTPRequest(auth:) odwołuje się do zmiennej dynamicznej $__githubAccessToken, więc jeśli pole zwróci błąd, w komunikacie o błędzie zostanie wydrukowany dosłowny ciąg "$__githubAccessToken" (nie jego wartość)

Aby zademonstrować ostatni punkt, podanie API GitHuba adresu URL nieistniejącego repozytorium "leoloso/NonExisting" powoduje błąd i otrzymujemy następującą odpowiedź (zwróć uwagę na auth: {password: $__githubAccessToken} w komunikacie o błędzie):

{
  "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
  }
}