Lección 21: No filtrar credenciales al conectarse a servicios
Esta consulta GraphQL recupera credenciales desde un valor de entorno, y evita que se impriman en la respuesta o logs, evitando así riesgos de seguridad:
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}"
}
})
}A continuación se explica cómo funciona esta consulta.
Cómo se podrían filtrar las credenciales
A menudo necesitamos proporcionar credenciales al conectarnos a servicios externos. Por ejemplo, la API REST de GitHub requiere un token de acceso para endpoints donde los datos son privados o se mutan:
query {
_sendJSONObjectItemHTTPRequest(input:{
url: "https://api.github.com/repos/GatoGraphQL/GatoGraphQL",
method: PATCH,
options: {
auth: {
password: "{ GITHUB_ACCESS_TOKEN }"
},
body: "{\"has_wiki\":false}"
}
})
}Tenemos que tener cuidado y evitar exponer nuestras credenciales:
- En la consulta GraphQL: Las credenciales nunca deben estar embebidas en el código fuente, ya que estarán en texto plano, creando un riesgo de seguridad
- En la respuesta GraphQL: Si el campo que se conecta al servicio produce un error, se añadirá un mensaje de error en la respuesta GraphQL bajo la entrada
errors; este mensaje podría imprimir el nombre del campo que falló junto con sus argumentos, imprimiendo así las credenciales - En los logs del servidor: Si las credenciales se acceden mediante una variable, y esta variable se proporciona como parámetro URL, entonces podría ser registrada en los logs del servidor web
Consulta GraphQL que evita filtrar credenciales
Esta consulta GraphQL pasa las credenciales a la API de GitHub mientras evita filtrar las credenciales:
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}"
}
})
}Esto es porque:
- Las credenciales se recuperan desde una variable de entorno
GITHUB_ACCESS_TOKEN, por lo que no necesitan estar embebidas en el código fuente - El campo
githubAccessTokense elimina con@remove, por lo que no se imprime en la respuesta - La entrada
_sendJSONObjectItemHTTPRequest(auth:)referencia la variable dinámica$__githubAccessToken, por lo que si el campo produce un error, será el string literal"$__githubAccessToken"el que se imprima en el mensaje de error (no su valor)
Para demostrar el último elemento, proporcionando la URL de un repositorio inexistente "leoloso/NonExisting" a la API de GitHub se produce un error, y obtenemos esta respuesta (nótese auth: {password: $__githubAccessToken} en el mensaje de 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
}
}