为什么使用PHP/cURL登录远程网站会引发400错误?

为什么使用PHP/cURL登录远程网站会引发400错误?,php,curl,Php,Curl,我正在尝试使用cURL编写一个PHP脚本,最终将在一个网站上运行一系列测试来检查内容,但是我似乎无法通过实际的部分登录 这是我目前正在尝试的代码: function loginCurl() { $url = "https://mywebsite.com/main/login"; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $login_page = curl_exec

我正在尝试使用cURL编写一个PHP脚本,最终将在一个网站上运行一系列测试来检查内容,但是我似乎无法通过实际的部分登录

这是我目前正在尝试的代码:

function loginCurl() {
    $url = "https://mywebsite.com/main/login";
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $login_page = curl_exec($ch);

    $csrf = preg_replace("/_csrf\"\svalue=\"/", "", substr($login_page, strpos($login_page, "_csrf"), 70));
    $username = "myEmail@mywebsite.com";
    $password = "abcde12345";

    $post_data = http_build_query(array( 
        '_csrf' => $csrf,
        'LoginForm' => array (
            'username' => $username,
            'password' => $password,
            'rememberMe' => 1
        )
    ));

    $HttpRequestHeaders = array(
        "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Language:en-US,en;q=0.8",
        "Cache-Control:max-age=0",
        "Connection:keep-alive",
        "Referer:https://mywebsite.com/main/login",
        "Upgrade-Insecure-Requests:1"
    );

    curl_setopt($ch, CURLOPT_URL,           "https://mywebsite.com/main/login");
    curl_setopt($ch, CURLOPT_POST,      true);
    curl_setopt($ch, CURLOPT_ENCODING,      '');
    curl_setopt($ch, CURLOPT_COOKIEJAR,     'cookie.txt');
    curl_setopt($ch, CURLOPT_COOKIEFILE,    'cookie.txt');
    curl_setopt($ch, CURLOPT_HTTPHEADER,    $HttpRequestHeaders);
    curl_setopt($ch, CURLOPT_HEADER,        true);
    curl_setopt($ch, CURLOPT_POSTFIELDS,    $post_data);
    curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
    $home_page = curl_exec($ch);
    curl_close($ch);

    return $home_page;
}
然而,每次我运行这段代码时,服务器都会响应一个HTTP400错误,尽管就我所见,我的请求并没有格式错误

当我在Chrome中打开开发者视图登录到实际的网站时,我可以打开网络下的登录页面,看到表单字段中传递的所有数据,这只是我代码中的5个内容。我不完全清楚为什么它会发送两次“rememberMe”复选框,但不管我发送多少次,它都会失败

这里的问题是,尽管现在我的表单数据和请求头与手动登录真实站点时的表单数据和请求头相同,但仍然会出现400错误

我想我真正的问题会分为几个不同的部分:

  • 有没有比使用cURL更好的远程登录网站的方法。据我所知,这是最好的办法
  • 如果这是最好的方法,那么有没有比查看开发人员视图记录的表单数据更好的方法来准确地告诉我在登录页面时发送了什么
  • 如果没有比我现在做的更好的方法,是否有可能网站错误地抛出了400个错误,而它真的应该是其他的?如果是这样的话,我将如何克服这个错误
  • 最后,因为我不知道它是否相关,下面是在开发者视图中记录的实际内容:

    Request headers: 
    
    Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/ *;q=0.8
    Accept-Encoding:gzip, deflate, br
    Accept-Language:en-US,en;q=0.8
    Cache-Control:max-age=0
    Connection:keep-alive
    Content-Length:212
    Content-Type:application/x-www-form-urlencoded
    Cookie:_csrf=1c43726b4d2c2298b2667f1c9b47d420ee594f2de08a9ce928ee155b174c624aa%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22_csrf%22%3Bi%3A1%3Bs%3A32%3A%22niY-LESiKNwZkfg7mclgTuBPDJeLACz2%22%3B%7D; PHPSESSID=itnbbfiph1gj2p9movpmi9pmb7
    Host:mywebsite.com
    Origin:https://mywebsite.com
    Referer:https://mywebsite.com/main/login
    Upgrade-Insecure-Requests:1
    User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
    
    Form Data:
    
    _csrf:Z0owR2dpVXQJI2lqKywGHSwERx0MDzJDCilcIDMcFyQjAFULJiovRg==
    LoginForm[username]:myEmail@mywebsite.com
    LoginForm[password]:abcdefg12345
    LoginForm[rememberMe]:0
    LoginForm[rememberMe]:1
    
    这是一个开发者视图,只有当你打开它时它才会跟踪

    这是它为网页的实际请求记录的内容:

    Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/ *;q=0.8
    Accept-Encoding:gzip, deflate, sdch, br
    Accept-Language:en-US,en;q=0.8
    Cache-Control:max-age=0
    Connection:keep-alive
    Cookie:_csrf=1c43726b4d2c2298b2667f1c9b47d420ee594f2de08a9ce928ee155b174c624aa%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22_csrf%22%3Bi%3A1%3Bs%3A32%3A%22niY-LESiKNwZkfg7mclgTuBPDJeLACz2%22%3B%7D; PHPSESSID=i4ppm1ggcq1cq6hkvilesggr10; _identity=2ca675db2d582db9f7ab8761cc0a07a14e2e2dece90386b11392b270637b0559a%3A2%3A%7Bi%3A0%3Bs%3A9%3A%22_identity%22%3Bi%3A1%3Bs%3A19%3A%22%5B1267%2Cnull%2C2592000%5D%22%3B%7D
    Host:mywebsite.com
    Referer:https://mywebsite.com/main/login
    Upgrade-Insecure-Requests:1
    User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
    
    这是包含csrf标记的sites HTML中的标记,当我回送csrf时,它会按原样显示。我不相信有解析错误,但是它在HTTP响应头中的显示方式不同,这对我来说很奇怪

    <input type="hidden" name="_csrf" value="T25ZVmZGZi4hBwB7KgM1RwQgLgwNIAEZIg01MTIzJH4LJDwaJwUcHA==">
    
    
    

    我希望有人能帮我解决这个问题,我已经做了几天了

    你犯了几个错误

    您的post_数据没有正确编码,
    [
    应该是
    %5B
    ,而
    ]
    应该是
    %5D
    ,如
    ”&LoginForm%5Busername%5D=“。urlencode($username)
    此外,您正在以相同的形式将LoginForm[rememberMe]重新定义为
    0
    1
    ,这肯定是不合法的

    但是,与其自己制作编码字符串(这很难看,而且(正如您刚刚证明的那样)容易出错,不如让
    http\u build\u query
    为您这样做:

    $post_data = http_build_query ( array (
            '_csrf' => $csrf,
            'LoginForm' => array (
                    'username' => $username,
                    'password' => $password,
                    'rememberMe' => 1 
            ) 
    ) );
    
    您说您
    “接受编码:gzip、deflate、sdch、br”
    ,但如果服务器决定使用这些编码,您没有提供任何代码来处理这些编码,并且您的loginCurl()函数无法告诉调用方服务器决定使用哪种编码。。最好的处理方法是自动为您提供curl句柄编码。不要自己发送
    accept encoding
    标题,而是将
    CURLOPT_encoding
    设置为emptstring
    '
    ,如果服务器决定使用编码,curl将自动
    accept encoding:
    -并自动解码

    您可以手动设置主机:头,不要这样做,让curl为您设置,就像
    curl\u init
    为您所做的那样(或者使用
    CURLOPT\u URL
    ),此外,您可能希望使用
    CURLOPT\u USERAGENT
    而不是自己设置USERAGENT头(如果手动设置,很容易忘记)

    此外,有些页面可能会检测到您伪造了
    推荐人
    标题,您可能只需设置CURLOPT_AUTOREFERER,并向您想要推荐人的url发出GET请求,而不是伪造推荐人标题:p

    问题
    远程登录网站是否有比使用cURL更好的方法。据我所知,这是最好的方法。
    -如果网站没有特定的api,那么根据我的经验,(lib)cURL是像浏览器一样登录的最好方法

    问题
    如果这是最好的方法,那么有没有比查看开发者视图中的表单数据更好的方法来准确地告诉我登录页面时发送的内容呢。
    -idk,但是99%的时间,chrome开发者工具对我来说都很好。有时,我不得不求助于
    Fiddler代理
    ReDox数据包编辑器
    ,当我想分析非浏览器应用程序时

    问题<代码>如果没有比我现在做的更好的方法,是否有可能该站点错误地抛出了400个错误,而它真的应该是其他错误?如果是这样,我将如何克服这个错误? -好吧,正如我上面所说的,你在这里做错了,但是当它在浏览器中工作,而在curl中不工作时,诀窍是将curl的请求与chrome的请求进行比较,并系统地消除基于curl的请求和基于浏览器的请求之间的每一个差异,直到它们实现为止e完全相等,并重试每一步。FiddlerProxy和netcat(在Linux中很常见,在windows中需要Cygwin)在这里派上了用场

    编辑9001:


    PS,查看您提供的测试数据,看起来像您的preg
    $csrf=preg\u replace(…
    code无法正确提取CSRF令牌。您的curl CSRF
    z0OWR2DPVXQJI2LKYWGHSWERX0MDZJDCILCIDMCFYQJAFULJIOVRG==
    与记录的CSRF
    1C43726B4D2C22982667F1C9B47D420EE594F2DE08A9CE928EEB174C624AA%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22%3Bi%3A1%3A%3Bs%3A32%3A%3A%3A%3A%jefglacz2%22%3B%7D

    如果你犯了一些错误,你就不应该