C++ libcurl请求LeetCode GraphQL接口遇CSRF验证403错误
LeetCode GraphQL接口CSRF验证失败403问题修复
你的代码存在5个直接触发CSRF校验失败的核心问题,逐一修复即可正常请求:
核心错误点
- POST请求载荷格式错误:LeetCode的GraphQL接口要求POST Body必须是包含
query字段的合法JSON结构,你直接将GraphQL查询字符串作为请求体发送,不符合接口要求,会直接被安全规则拦截。 - CSRF传参缺失:LeetCode的CSRF校验规则要求,除了Cookie中携带
csrftoken,还必须在请求头中显式传递x-csrftoken字段,值与Cookie中的csrftoken完全一致,你仅在Cookie中传入token、未加对应请求头,必然触发验证失败。 - CSRF Cookie获取逻辑错误:直接向
/graphql端点发GET请求无法拿到合法的CSRF Cookie,必须先访问LeetCode公开页面(如题目列表页、首页),服务端才会下发和会话绑定的有效csrftoken。 - 会话Cookie丢失:POST请求阶段你手动构造Cookie仅传入
csrftoken,丢弃了GET阶段拿到的其他关联会话Cookie,服务端无法将token和当前请求会话绑定,验证无法通过。 - 请求头格式错误:你写的
User-Agent头中间存在硬换行,属于非法HTTP头格式,会导致服务端识别请求异常。
修复步骤
- 调整请求流程
- 第一步发送GET请求到
https://leetcode.com/problemset/all/,统一开启libcurl的自动Cookie管理(CURLOPT_COOKIEJAR和CURLOPT_COOKIEFILE指向同一个本地Cookie文件),不要手动拼接Cookie字符串,让libcurl自动维护所有会话Cookie。 - GET请求完成后,从本地Cookie文件中解析出
csrftoken字段的值。 - 第二步发送POST请求到
https://leetcode.com/graphql,复用同一个Cookie文件配置,libcurl会自动携带所有关联会话Cookie,不需要手动构造Cookie头。
- 第一步发送GET请求到
- 修正POST请求配置
- 将GraphQL查询语句包装为合法JSON格式,结构为
{"query": "你的GraphQL查询内容"},注意转义特殊字符。 - 在POST请求的头列表中新增
x-csrftoken: 你解析到的csrftoken值头,同时补充Origin: https://leetcode.com头模拟浏览器请求。 - 删除
User-Agent头中的硬换行,保证所有HTTP头为单行合法格式。 - 不管GET还是POST请求,都统一挂载通用请求头,不要仅在POST分支设置HTTP头。
- 将GraphQL查询语句包装为合法JSON格式,结构为
- 资源回收补充:请求结束后主动释放
curl_slist头列表、关闭打开的文件句柄,避免内存泄漏。
修正后的核心参考代码
void request(bool isGet = true, std::string csrf = "") { // 将GraphQL查询包装为合法JSON结构,使用原始字符串避免转义问题 static std::string query = R"({ "query": "query allQuestionsRaw{ allQuestions: allQuestionsRaw { title titleSlug translatedTitle questionId questionFrontendId status difficulty isPaidOnly categoryTitle __typename } }" })"; curl_slist* hdrsl = NULL; hdrsl = curl_slist_append(hdrsl, "Content-Type: application/json"); // 修复User-Agent换行问题 hdrsl = curl_slist_append(hdrsl, "User-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"); hdrsl = curl_slist_append(hdrsl, "Accept: */*"); hdrsl = curl_slist_append(hdrsl, "Host: leetcode.com"); hdrsl = curl_slist_append(hdrsl, "Origin: https://leetcode.com"); // POST请求额外添加x-csrftoken头 if (!isGet && !csrf.empty()) { std::string csrfHeader = "x-csrftoken: " + csrf; hdrsl = curl_slist_append(hdrsl, csrfHeader.c_str()); } CURL* curl = curl_easy_init(); FILE* fd = fopen("tasks.json", "w"); // GET阶段请求题目列表页拿Cookie,POST阶段请求GraphQL接口 if (isGet) { curl_easy_setopt(curl, CURLOPT_URL, "https://leetcode.com/problemset/all/"); } else { curl_easy_setopt(curl, CURLOPT_URL, "https://leetcode.com/graphql"); } curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 所有请求统一挂载请求头 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrsl); curl_easy_setopt(curl, CURLOPT_REFERER, "https://leetcode.com/problemset/all/?page=2"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // 统一配置Cookie自动管理,GET/POST共用同一个Cookie文件维护会话 curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "leetcode_cookie.txt"); curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "leetcode_cookie.txt"); if (!isGet) { curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query.c_str()); } curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fd); curl_easy_perform(curl); // 主动释放资源 curl_slist_free_all(hdrsl); curl_easy_cleanup(curl); fclose(fd); }
如果按上述配置修改后仍遇到拦截,可以先在浏览器中正常访问LeetCode通过人机验证,再将浏览器生成的有效Cookie导出到代码使用的
leetcode_cookie.txt文件中再发起请求。
内容的提问来源于stack exchange,提问作者WelcomeToMyTutorial
相关产品推荐
相关产品推荐

