从 curl 调用 GraphQL 端点意外失败

Posted

技术标签:

【中文标题】从 curl 调用 GraphQL 端点意外失败【英文标题】:Invoking GraphQL endpoints from curl failing unexpectedly 【发布时间】:2021-12-07 18:40:04 【问题描述】:

多年来,我一直在使用 REST API(作为消费者和开发人员),并且刚刚开始第一次使用 GraphQL API,具体来说,BurpSuite's Enterprise GraphQL API。我喜欢它,但我肯定遗漏了一些关键概念。

我正在尝试访问他们的GetScan 端点:

curl -k -i -H "Content-Type: application/json" -H "Authorization: <MY_API_KEY>" -X GET -d ' "query": "query GetScan ($id: ID!)  scan(id: $id)  id status agent  id name  site_application_logins  login_credentials  label username  recorded_logins  label   audit_items  id issue_counts  total  number_of_requests  scan_configurations  id name   "' 'https://mybsee.example.com/graphql/v1'

为了便于阅读,graphql 查询也是:

query GetScan ($id: ID!) 
  scan(id: $id) 
    id status
    agent  id name 
    site_application_logins 
      login_credentials  label username 
      recorded_logins  label 
    
    audit_items 
      id
      issue_counts  total 
      number_of_requests
    
    scan_configurations  id name 
  
,
variables 
    $agent: "Firefox"

&lt;MY_API_KEY&gt; 以上在我运行时具有实际价值(显然,我不能在此处发布)。与 URL 相同(我有一个 BurpSuite 的本地版本正在运行,例如 mybsee.example.com)。

当我运行 curl 时,我得到以下输出:

About to execute: query GetScan ($id: ID!)  scan(id: $id)  id status agent  id name  site_application_logins  login_credentials  label username  recorded_logins  label   audit_items  id issue_counts  total  number_of_requests  scan_configurations  id name   
HTTP/2 200 
date: Wed, 20 Oct 2021 23:33:50 GMT
x-frame-options: DENY
<lots of response headers omitted>

"errors":["message":"Unexpected exception occurred. Check logs for more details.","extensions":"code":77]

我无权访问服务器日志,因此我试图排除客户端的问题(我对GetScan 的调用不正确)。有人觉得有什么不对吗?我是否应该向查询中的任何字段注入值,例如idstatus 等?如果是这样,我如何(具体地)将查询更改为有效?我也不确定是否需要将"query" JSON 字段名称附加到实际查询中。

感谢您在此提供的所有帮助。

更新

我确实意识到这不是一个完美可以回答的问题,因为不幸的是 BurpSuite API 是专有的,除非您购买产品或经历大量 rigamarole,否则您无法从他们那里获得 API 密钥获得免费试用许可证。

但更重要的是,我不是在这里寻找任何人来钓鱼,我希望有人可以教我如何钓鱼。实际上,除了这个之外,我还需要与更多 GraphQL 端点集成,但如果有人能告诉我构造一个查询的正确方法,我可以从那里获取。

【问题讨论】:

你错过了-H "Authorization:结尾处的双引号。 感谢@chicks (+1) 这是我的复制粘贴错误;现在修好了! 我在您的查询中没有看到任何变量。你在哪里传递特定的 id? (顺便说一句,将 graphql 查询格式化为多行正确缩进的文本会使其更易于阅读)。 不过,基本上:GetScan 需要提供一个 id 作为参数——ID! 中的 ! 表示这是强制性的。 【参考方案1】:

最安全的方法是使用 jq 生成正确转义的 JSON,其中包含您的查询及其参数。

构建一个自动化的函数:

# usage: gqlQuery http://ENDPOINT/graphql "$query" arg1=val1 arg2=val2 ...
gqlQuery() 
  local endpoint query postBody
  local -a jqArgs=( )
  endpoint=$1; shift || return
  query=$1; shift || return
  while (( $# )); do
    case $1 in

      # the variable name "query" is used for passing in, well, the query
      query=*)  echo "ERROR: variable name query is reserved" >&2; return 1;;

      # 'foo=JSON:["bar", "baz"]' passes ["bar", "baz"] as a list, not a string
      *=JSON:*) jqArgs+=( --argjson "$1%%=*" "$1#*=" ) ;;

      # without JSON:, everything gets passed as a string to the child
      *=*)      jqArgs+=( --arg "$1%%=*" "$1#*=" ) ;;

      # arguments without a = are not recognized
      *)        echo "ERROR: Argument $1 not well-formed" >&2; return 1;;
    esac
    shift
  done
  postBody=$(jq -nc \
    --arg query "$query" \
    "$jqArgs[@]" \
    ' "query": $query, "variables": ($ARGS.named | del(.query)) '
  ) || return
  curl --fail -XPOST \
    -H 'Content-Type: application/json' \
    -d "$postBody" "$endpoint"

...你可以使用这样的函数:

getScanQuery='query GetScan ($id: ID!)  scan(id: $id)  id status agent  id name  site_application_logins  login_credentials  label username  recorded_logins  label   audit_items  id issue_counts  total  number_of_requests  scan_configurations  id name   '
myUrl=https://mybsee.example.com/graphql/v1
scanIdToRetrieve=1000 # replace with whatever is appropriate

result=$(gqlQuery "$myUrl" "$getScanQuery" id="$scanIdToRetrieve")

【讨论】:

啊,好吧,我想我明白了!所以让我们看一下getScanQuery 变量的第一部分:GetScan ($id: ID!) scan(id: $id).../ 这是否翻译为“有一个GetScan 端点接受一个必需 参数$id,即类型为“ID”(我假设 ID 是 GQL 领域中的一种类型)?然后在查询正文中引用 $id 的任何地方,从 variables 映射传入的值被替换?是这样吗有效吗?我也认为它的gqlQuery 无处不在,而不是golQuery,对吧?再次感谢! 很正确golQuery 是一个错字,也很正确ID 是一种类型(类型可以定义为查询文本的一部分,当它们没有预定义时;我没有临时记住 ID 是否是预定义类型 - 如果不是,那么您可能需要在查询正文中包含它的定义。 我有点犹豫是否要确认“在正文中引用 $id 的所有位置”位的唯一原因是GetScan 查询的参数决定了在引用中填写的内容块,但这是一个没有太大区别的区别;您描述的方式通常非常接近:) 在graphql.org/learn/schema中查找,ID确实是预定义类型。 顺便说一句,上面gqlQuery 的版本是一次性的;如果我正在构建一些用于生产用途的东西,我可能会在 case 语句的顶部添加类似 -X*) curlArgs+=( "$1%-X );; 的内容(在顶部定义 local -a curlArgs=( ) 并在底部使用 curl "$curlArgs[@]" 之后)让调用者将额外的参数传递给curl,而无需更改函数。【参考方案2】:
curl -k -i -H "Content-Type: application/json" -H "Authorization: Bearer <API_TOKEN>" -X POST -d " \"query\": \"$string\"" '<API_URL>'

您几乎做对了所有事情。只需将 HTTP 方法更改为 POST 而不是 GET。 GET 将尝试获取整个页面,而 POST 将与 GraphQL API 交互。

尽管如此,GraphQL serving over HTTP page 中声明 GET 方法也应该在某些限制下与查询一起使用。有关更多信息,请参阅链接。

还可以考虑访问导致我达到此决议的其他网页;Introduction to GraphQL on GitHubMaking GraphQL Requests using HTTP MethodsBurp Suite Enterprise GraphQL forum discussion with the same Error code of yours

【讨论】:

感谢 @Ido (+1),如果您不介意的话,还有两个后续问题:(1) 是 GraphQL 始终 POST,甚至查询?如果不是,你为什么建议我在这里使用 POST? (2) 我是否需要将变量值注入到我的查询中?如果是这样,我如何(具体来说,举个例子)实现这一点?再次感谢! @hotmeatballsoup,是的,它总是一个 POST,无论你是在做查询还是突变。 在示例中展示curl -k (--insecure) 其他人可能不假思索地效仿是一个真的坏主意。除此之外,OP 的 GraphQL 查询需要一个 id 变量,但他们没有传递一个。

以上是关于从 curl 调用 GraphQL 端点意外失败的主要内容,如果未能解决你的问题,请参考以下文章

尝试从反应应用程序连接到 graphcool 中继 API 时出现此错误:模块构建失败:错误:没有有效的 GraphQL 端点

从 React 向 GraphQL 服务器进行查询时出现意外错误 [重复]

Graphql API 响应返回意外数据

使用赛普拉斯 .request 调用 GraphQL 端点

多部分/表单数据请求失败。直播意外结束

为啥 GraphQL 查询返回 null?