使用 PHP curl 和 CSRF 令牌登录


Login with PHP curl and CSRF token

我想从PHP脚本登录到另一个网站,但我总是得到这个回复:

403 Error: CSRF token mismatch

我从网站上的隐藏字段中提取CSRF令牌,但似乎这是错误的。这是我的代码:

$username = "testuser";
$password = "testpass";
$path = "c:''test''";
$url="http://itw.me/login";
$field='_csrf';
$html=file_get_contents( $url );
libxml_use_internal_errors( true );
$dom=new DOMDocument;
$dom->validateOnParse=false;
$dom->recover=true;
$dom->formatOutput=false;
$dom->loadHTML( $html );
libxml_clear_errors();
$xpath=new DOMXPath( $dom );
$col=$xpath->query('//input[@name="'.$field.'"]');
foreach( $col as $node ) $csrftoken=$node->getAttribute('value');
echo "-".$csrftoken."-";
$postinfo = "email=".$username."&password=".$password."&_csrf=".$csrftoken;
$cookie_file_path = $path."/cookie.txt";
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_NOBODY, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file_path);
curl_setopt($ch, CURLOPT_COOKIE, "cookiename=0");
curl_setopt($ch, CURLOPT_USERAGENT,"Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.12) Gecko/20050915 Firefox/1.0.7");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_REFERER, $_SERVER['REQUEST_URI']);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postinfo);
curl_exec($ch);
curl_setopt($ch, CURLOPT_URL, "http://itw.me");
$html = curl_exec($ch);
print($html);
curl_close($ch);

您正在使用 file_get_contents 发出第一个请求,并使用 cURL 发出第二个请求。

您已经为第二个请求设置了一个 cookie jar,但没有为第一个请求设置。因此,这两个请求中的每一个都在启动一个新会话。

CSRF 令牌由您向其发出请求的服务器存储在会话中,因此您获得的第一个请求的令牌与第二个请求的会话中的令牌不匹配。

您需要:

  • 一致地使用 cURL
  • 对两个请求使用相同的饼干罐

现在它正在工作了!

$username = "user";
$password = "pass";
$url = "http://url.com/login";
$cookie= "c:''cookies.txt";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) die(curl_error($ch));
$dom = new DomDocument();
$dom->loadHTML($response);
$tokens = $dom->getElementsByTagName("meta");
for ($i = 0; $i < $tokens->length; $i++)
{
    $meta = $tokens->item($i);
    if($meta->getAttribute('name') == 'csrf-token')
    $token = $meta->getAttribute('content');
}
$postinfo = "email=".$username."&password=".$password."&_csrf=".$token;
echo $token; //debug info
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postinfo);
$html = curl_exec($ch);
    print($html);
if (curl_errno($ch)) print curl_error($ch);
    curl_close($ch);
为每个

会话生成的CSRF令牌。阅读你的问题的第一条评论(@walkingRed条评论)(评论已删除 - cURL CSRF 令牌 - 这是解决方案)。有解决你的问题。

您应该在另一个网站上开始会话,之后您可以获得正确的 csrf 令牌。

在每个请求中使用 cookie 以定义外部请求始终在同一会话中非常重要。

例:

您首次请求获取CSRF令牌。在外部网站上为您的请求启动新会话并生成csrf令牌。

然后,您为此重新开始新会话再次启动新会话,

并再次为这个新会话生成新的csrf令牌,但不使用cookie。