Tutorial skema
Tutorial skemaPelajaran 21: Tidak membocorkan kredensial saat terhubung ke layanan

Pelajaran 21: Tidak membocorkan kredensial saat terhubung ke layanan

Query GraphQL ini mengambil kredensial dari nilai lingkungan, dan menghindari agar tidak tercetak dalam respons atau log, sehingga menghindari risiko keamanan:

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

Berikut adalah penjelasan tentang cara kerja query ini.

Bagaimana kredensial bisa bocor

Kita sering perlu menyediakan kredensial saat terhubung ke layanan eksternal. Misalnya, REST API GitHub memerlukan token akses untuk endpoint di mana data bersifat privat atau dimutasi:

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

Kita perlu berhati-hati dan menghindari pemaparan kredensial kita:

  • Dalam query GraphQL: Kredensial tidak boleh pernah disematkan dalam kode sumber, karena akan berada dalam teks biasa, menciptakan risiko keamanan
  • Dalam respons GraphQL: Jika field yang terhubung ke layanan menghasilkan error, pesan error akan ditambahkan dalam respons GraphQL di bawah entri errors; pesan ini mungkin mencetak nama field yang gagal beserta argumennya, sehingga mencetak kredensial
  • Dalam log server: Jika kredensial diakses melalui variabel, dan variabel ini disediakan sebagai parameter URL, maka mungkin akan dicatat dalam log server web

Query GraphQL yang menghindari kebocoran kredensial

Query GraphQL ini meneruskan kredensial ke API GitHub sambil menghindari kebocoran kredensial:

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

Hal ini karena:

  • Kredensial diambil dari variabel lingkungan GITHUB_ACCESS_TOKEN, sehingga tidak perlu disematkan dalam kode sumber
  • Field githubAccessToken di-@remove, sehingga tidak tercetak dalam respons
  • Input _sendJSONObjectItemHTTPRequest(auth:) mereferensikan variabel dinamis $__githubAccessToken, sehingga jika field menghasilkan error, yang akan dicetak dalam pesan error adalah string literal "$__githubAccessToken" (bukan nilainya)

Untuk mendemonstrasikan item terakhir, memberikan URL repositori yang tidak ada "leoloso/NonExisting" ke API GitHub menghasilkan error, dan kita mendapatkan respons ini (perhatikan auth: {password: $__githubAccessToken} dalam pesan error):

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