スキーマチュートリアルレッスン21: サービスへの接続時に認証情報を漏洩させない
レッスン21: サービスへの接続時に認証情報を漏洩させない
このGraphQLクエリは、環境変数から認証情報を取得し、レスポンスやログに出力されないようにすることで、セキュリティリスクを回避します:
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}"
}
})
}以下は、このクエリの仕組みについての説明です。
認証情報が漏洩する可能性のある状況
外部サービスへ接続する際、認証情報の提供が必要になることがよくあります。たとえば、GitHubのREST APIでは、データがプライベートであるか変更を行うエンドポイントにはアクセストークンが必要です:
query {
_sendJSONObjectItemHTTPRequest(input:{
url: "https://api.github.com/repos/GatoGraphQL/GatoGraphQL",
method: PATCH,
options: {
auth: {
password: "{ GITHUB_ACCESS_TOKEN }"
},
body: "{\"has_wiki\":false}"
}
})
}認証情報を公開しないよう注意する必要があります:
- GraphQLクエリ内: 認証情報をソースコードに直接埋め込んではなりません。平文で記録されるためセキュリティ上の危険が生じます
- GraphQLレスポンス内: サービスへ接続するフィールドがエラーを発生させた場合、GraphQLレスポンスの
errorsエントリにエラーメッセージが追加されます。このメッセージには失敗したフィールド名と引数が含まれる場合があり、認証情報が出力されてしまう可能性があります - サーバーログ内: 認証情報が変数経由でアクセスされ、その変数がURLパラメータとして提供される場合、Webサーバーのログに記録される可能性があります
認証情報の漏洩を回避するGraphQLクエリ
このGraphQLクエリは、認証情報を漏洩させることなくGitHubのAPIへ渡します:
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}"
}
})
}その理由は以下のとおりです:
- 認証情報は環境変数
GITHUB_ACCESS_TOKENから取得されるため、ソースコードに埋め込む必要がありません - フィールド
githubAccessTokenは@removeによって削除されるため、レスポンスに出力されません _sendJSONObjectItemHTTPRequest(auth:)の入力は動的変数$__githubAccessTokenを参照しているため、フィールドがエラーを生成した場合、エラーメッセージに出力されるのはリテラル文字列"$__githubAccessToken"であり、その値ではありません
最後の点を示すために、存在しないリポジトリ "leoloso/NonExisting" のURLをGitHubのAPIへ渡すとエラーが発生し、次のようなレスポンスが返されます(エラーメッセージ内の auth: {password: $__githubAccessToken} に注目してください):
{
"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
}
}