PHP Curl提交学生ID与CSRF令牌至考勤系统失败求助
解决PHP Curl提交Django考勤系统数据失败的问题
我希望实现本校考勤系统的自动化操作,该网站需要提交
student_id(学生ID)和csrfmiddlewaretoken两个数据字段,但使用PHP Curl提交数据时始终失败,尝试了以下两种方法均未成功,现寻求解决方案。
原始方法1
<?php $url = "http://x.com"; $username = "111"; $csrf_token_field_name = "csrfmiddlewaretoken"; $params = array( "student_id" => $username ); $token_cookie= realpath("test.txt"); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/32.0.1700.107 Chrome/32.0.1700.107 Safari/537.36'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_COOKIEJAR, $token_cookie); curl_setopt($ch, CURLOPT_COOKIEFILE, $token_cookie); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); $response = curl_exec($ch); if (curl_errno($ch)) die(curl_error($ch)); libxml_use_internal_errors(true); $dom = new DomDocument(); $dom->loadHTML($response); libxml_use_internal_errors(false); $tokens = $dom->getElementsByTagName("input"); for ($i = 0; $i < $tokens->length; $i++) { $meta = $tokens->item($i); if($meta->getAttribute('name') == 'csrfmiddlewaretoken') $t = $meta->getAttribute('value'); } if($t) { $csrf_token = file_get_contents(realpath("another-cookie.txt")); $postinfo = ""; foreach($params as $param_key => $param_value) { $postinfo .= $param_key ."=". $param_value . "&"; } $postinfo .= $csrf_token_field_name ."=". $t; $headers = array(); $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,"; $header[] = "Cache-Control: max-age=0"; $header[] = "Connection: keep-alive"; $header[] = "Keep-Alive: 300"; $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"; $header[] = "Accept-Language: en-us,en;q=0.5"; $header[] = "Pragma: "; $headers[] = "X-CSRF-Token: $t"; $headers[] = "Cookie: $token_cookie"; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6"); curl_setopt($ch, CURLOPT_COOKIEJAR, $token_cookie); curl_setopt($ch, CURLOPT_COOKIEFILE, $token_cookie); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postinfo); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_REFERER, $url); curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate"); curl_setopt($ch, CURLOPT_AUTOREFERER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 260); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_VERBOSE, true); ob_start(); $html = curl_exec($ch); $result = curl_getinfo($ch); ob_get_clean(); echo "<pre>"; print_r($result); echo "</pre>"; print($html); if (curl_errno($ch)) print curl_error($ch); curl_close($ch); } ?>
原始方法2
<?php include "simple_html_dom.php"; $ch = curl_init("http://x.com"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt'); $response = curl_exec($ch); $dom = new DOMDocument; $dom->loadHTML($response); $tags = $dom->getElementsByTagName('input'); for($i = 0; $i < $tags->length; $i++){ $grab = $tags->item($i); if($grab->getAttribute('name') === 'csrfmiddlewaretoken'){ $token = $grab->getAttribute('value'); } } echo $token; $data = array( "csrfmiddlewaretoken"=> $token, "student_id"=> '123' ); curl_setopt($ch, CURLOPT_URL, "http://x.com"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookie.txt'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); $response = curl_exec($ch); echo $response; ?>
核心问题分析
方法1的错误点
- 手动拼接POST数据未做
urlencode处理,若student_id含特殊字符会导致参数解析失败 - 错误将Cookie文件路径写入
Cookie请求头,Curl会自动通过CURLOPT_COOKIEJAR和CURLOPT_COOKIEFILE管理Cookie,无需手动设置该头 - 额外添加的
X-CSRF-Token头不符合Django校验逻辑,Django优先通过表单字段或Cookie中的csrftoken验证,该头属于冗余操作 - 读取
another-cookie.txt的操作完全多余,Cookie已由Curl自动维护
方法2的错误点
- 未设置
CURLOPT_COOKIEFILE,导致第二次POST请求无法携带第一次GET请求获取的Cookie(Django需要Cookie中的csrftoken与表单字段配合验证) - 未设置
User-Agent请求头,部分网站会拦截无UA的请求 - 未设置
Referer头,Django可能会校验请求来源 - 引入
simple_html_dom.php但未使用,属于冗余代码
修正后的可行代码
<?php $url = "http://x.com"; $student_id = "123"; // 替换为实际学生ID $cookie_file = realpath("cookie.txt"); // 第一步:获取页面和CSRF Token,同时保存Cookie $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', CURLOPT_COOKIEJAR => $cookie_file, CURLOPT_COOKIEFILE => $cookie_file, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_SSL_VERIFYPEER => 0, ]); $response = curl_exec($ch); if (curl_errno($ch)) { die("请求失败:" . curl_error($ch)); } // 解析CSRF Token libxml_use_internal_errors(true); $dom = new DOMDocument(); $dom->loadHTML($response); libxml_use_internal_errors(false); $csrf_token = null; $inputs = $dom->getElementsByTagName("input"); foreach ($inputs as $input) { if ($input->getAttribute("name") === "csrfmiddlewaretoken") { $csrf_token = $input->getAttribute("value"); break; } } if (!$csrf_token) { die("未找到CSRF Token"); } // 第二步:提交POST请求 $post_data = [ "csrfmiddlewaretoken" => $csrf_token, "student_id" => $student_id ]; curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($post_data), // 自动处理URL编码 CURLOPT_REFERER => $url, // 设置来源页面 CURLOPT_FOLLOWLOCATION => true, // 跟随跳转 ]); $submit_response = curl_exec($ch); if (curl_errno($ch)) { die("提交失败:" . curl_error($ch)); } // 输出结果 echo "<pre>"; print_r(curl_getinfo($ch)); echo "</pre>"; echo $submit_response; curl_close($ch); ?>
关键优化点
- 使用
curl_setopt_array批量设置参数,代码更简洁易维护 - 用
http_build_query处理POST数据,自动完成URL编码,避免手动拼接的错误 - 同时设置
CURLOPT_COOKIEJAR和CURLOPT_COOKIEFILE,确保Cookie在两次请求间正确传递 - 添加标准的
User-Agent和Referer头,模拟正常浏览器请求 - 简化CSRF Token的解析逻辑,提前终止循环提升效率
内容的提问来源于stack exchange,提问作者nasser mansoor
相关产品推荐
相关产品推荐

