在带有 Cookie 的安全站点上使用 Indy TIdHTTP Post 方法

Using Indy TIdHTTP Post Method on Secure Site with Cookies

我正在尝试使用 TIdHTTP.Post() 方法在我无法理解的网站上提交表单。我已经尝试了几次迭代和更改我的代码,并且遇到了我需要获得帮助的障碍。我对 TIdHTTP 及其用法还比较陌生,所以我请求原谅我的代码中的任何愚蠢之处。

到目前为止,我已经能够使用 TIdHTTP.Get() 方法从站点获取 HTML 代码。然后我检查 HTML 中的 <form> 代码,并设计了以下代码以将该表单提交到网站。

我不知道我的代码不能正常工作是因为我没有正确使用 Post() 方法,还是因为我没有正确使用 CookieManager。我收到的只是执行时的 "Internal Server Error"。

有趣的是,手动登录的网站要求您输入您的帐号,出生日期和密码,但我在HTML中看到的表单仅包含两个提交变量...用户名(似乎是 Acct 和 DOB 的组合)并提交。所以我不明白 how/where 它处理或发布密码变量?

这是我在 Delphi 中的当前代码:

procedure TMSBS_App_GUI.SubmitClick(Sender: TObject);
Var
  Response : String;
  ResponseSet : TStringStream;
  Params : TStringList;
  IdHttp : TIDHttp;
  IdSSL : TIdSSLIOHandlerSocketOpenSSL;
  CookieMonster : TidCookieManager;
begin
  Params := TStringList.Create;
  Params.Add('username=' + 'username');
  Params.Add('submit.value=' + 'submit');
  idhttp := TIdhttp.Create;
  idhttp.AllowCookies := True;
  CookieMonster := TiDCookieManager.Create;
  idHttp.CookieManager := CookieMonster;
  idSSLOpenSSLHeaders.Load;
  IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  idHttp.ReadTimeout := 30000;
  idHttp.IOHandler := idSSL;
  idHttp.Get('https://' + website);
  idhttp.Request.ContentType := 'application/x-www-form-urlencoded';
  idhttp.Request.Referer := 'http://' + website;
  idSSL.SSLOptions.Method := sslvTLSv1;
  idSSL.SSLOptions.Mode := sslmUnassigned;
  ResponseSet := TStringStream.Create(nil);
  Try
    Memo1.Text := idHttp.Post('https://' + website,Params);
  Finally
    Params.Free;
    ResponseSet.Free;
  End;
end;

这是网页:

<!-- SiteMinder Encoding=ISO-8859-1; --> 
<!-- FCC File : (generic) caloglfn.fcc version 1.4-->

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE"> 
<link rel="stylesheet" type="text/css" href="styles.css" /> 

<!-- Cross-frame scripting prevention: This code will prevent this page from being encapsulated within HTML frames. Remove, or comment out, this code if the functionality that is contained in this SiteMinder page is to be included within HTML frames. --> 
<SCRIPT type="text/javascript" src="https://ff.kis.v2.scr.kaspersky-labs.com/D0501246-9A02-314D-B50C-0C6D353C6332/main.js" charset="UTF-8"></script><link rel="stylesheet" crossorigin="anonymous" href="https://ff.kis.v2.scr.kaspersky-labs.com/2336C353D6C0-C05B-D413-20A9-6421050D/abn/main.css"/><script> 
if (top !=self) 
top.location=self.location; 
</SCRIPT> 

<title>Member/Pensioner Services Online Login</title> 
<script> 
function submit_form() 
{ 
  document.mos_form.username.value = document.mos_form.pUserID.value + document.mos_form.pDOB.value  

  document.mos_form.submit() 
} 
</script> 

</head> 
<body> 
<div id="wrapper"> 
    <div id="help">
     <a style="border-bottom:none;" href="mso_pso_access_help.html" target="_blank"><img STYLE="border:none;" src="Help_button.png" alt="Help" align="right"> </a>
   </div>
    <div id="header"> 
    <img class="crest" src="crest.png" alt="Crest - Superannuation Corporation" align="left"> 
    <img class="logo" src="mso_pso.png" alt="Pensioner Services Online (PSO) and Member Services Online (MSO)" align="right"> 
  </div> 
    <div id="toplinks"> 
    </div> 

<div id="form"> 
<form name="mos_form" method="POST" enctype="application/x-www-form-urlencoded" autocomplete="off"> 
<input type="hidden" name="autherrmsg" value="Login failed. Please try again."/> 

  <table width="100%" border="0" cellspacing="2" cellpadding="2">
    <tr> 
        <td align="left">
            <H1>
            <span style="color: #3E842E;">Member Access</span>
        </H1>
    </td>
    </tr>

    <tr>
        <td>
            <p><font color="red">  </font></p> 
            <p>   
               To gain access to the complete range of online services, please enter your Membership Number, Date of Birth and Password below. 
            </p>
            <p> If you need any help, click on Help in the top right-hand corner of this screen.</p>
            <p> </p> 
        </td>        
    </tr>
  </table>

  <label for="hidden_username"></label> 
  <input type="hidden" name="username"> 
  <label for="hidden_url"></label> 
  <input type="hidden" name="url" value="<ERROR_INFORMATION>" READONLY> 
  <label for="hidden_proxy"></label> 
  <input type="hidden" name="proxypath" value="<PROXY_PATH>"> 

  <table width="900" border="0">
        <tr height="38" valign="top">
            <td width="200px" align="right" > <label for="mem_num" id="mem_num_label">Membership Number </label> </td>
            <td width="200px" > <input id="pUserID" type="text" name="pUserID" value="" size="27" maxlength="13" id="mem_num"> </td>
            <td width="500px">
        &nbsp;&nbsp;
                <img STYLE="border:none;" src="mso_pso_question.png" onmouseover="this.style.cursor = 'help';" 
                 title="Please enter your Membership Number.
This is either your Employment Number or Pension Reference Number as found on our correspondence to you.
If this is not available please Contact us.">
        </td>
        </tr>
        <tr height="38" valign="top">
            <td width="200px" align="right" > <label for="dob" id="dob_label">Date of Birth </label> </td>
            <td width="200px" > <input type="text" name="pDOB" value="" size="27" maxlength="8" id="dob" placeholder="DDMMYYYY"> </td>
            <td width="500px">
            &nbsp;&nbsp;
                <img STYLE="border:none;" src="mso_pso_question.png" onmouseover="this.style.cursor = 'help';" 
                 title="Please enter your date of birth in this format: DDMMYYYY (e.g. 01021955).">
        </td>
        </tr>
        <tr height="38" valign="top">
            <td width="200px" align="right" > <label for="acc_num" id="acc_num_label">Password</label> </td>
            <td width="200px"> <input type="PASSWORD" name="password" value="" size="27" maxlength="30" id="acc_num"> </td>
            <td width="500px">
            &nbsp;&nbsp;
                <img STYLE="border:none;" src="mso_pso_question.png" onmouseover="this.style.cursor = 'help';"
                  title='Please enter your Password.
If you have forgotten your Password, use the &#34I&#39ve forgotten my password&#34 link to reset your access credentials.
If you need to contact us, our details are available via the contact us button at the top right-hand corner of this screen.'>

        </td>
        </tr>
        </tr>
                   <tr height="38" valign="top">
                        <td width="200px" width=200></td>
                        <td width="200px" align="right"> <a href="https://www.*****.au/no-scheme-provided/register-or-reset"> I've forgotten my password </a> &nbsp;&nbsp; </td>
                        <td width="500px">
                   </tr>
        <tr height="38" valign="top">
            <td colspan=2 align="right"><a href="https://www.*****.au/register-or-reset/no-scheme-provided"> Register</a>&nbsp&nbsp <input type=button onclick=javascript:submit_form() value=Login> &nbsp;&nbsp;</td>
        </tr>
        <tr>
  </table>

  <script language="JavaScript"> 
              <!-- the script here sets the focus on UserID field 
              document.mos_form.pUserID.focus(); 
              document.mos_form.pUserID.select(); 
              function enter(e) 
              { 
                 if (navigator.appName == "Netscape") 
                    whichASCII = e.which; 
                 else  
                    whichASCII = event.keyCode 
                 if (whichASCII == 13 ) 
                 { 
                    submit_form(); 
                 } 
              } 
                 document.onkeypress = enter; 
                 if (navigator.appName == "Netscape") 
                     document.mos_form.pAccessCode.onkeypress = enter; 
              // End of script --> 
              </script> 
               <!-- SiteMinder Variables START --> 

               <input type=hidden name=target value="http://website"> 
               <input type=hidden name=smauthreason value="0"> 
               <input type=hidden name=smagentname value="boFynyFE9jczy7ra1lzqLmXPeVc9xLptAWQSI9ksks1Hx/oGQmJxQA7Fy25/Xt9X"> 

               <!-- SiteMinder Variables END --> 


</FORM> 
</div> 
        <div id="footer"> 
            <table width=100% border=0 cellspacing="0" cellpadding="0"> 
              <tr>  
                <td height="30px"  align="left" valign="middle" bgcolor="#3E842E">&nbsp;&nbsp;  
                <a href="http://www.*****.au/privacy" title="Privacy policy" target="_blank" class="wlink">Privacy</a> |  
                <a href="http://www.*****.au/disclaimer" title="Disclaimer" target="_blank" class="wlink">Disclaimer</a> 
                </td> 
              </tr> 
              <tr>  
                <td height="30px" colspan="2" valign="top" bgcolor="#949599" class="footer"><p class="footer"><span class="bold">Superannuation Company</span> ABN: ## ### ### ### AFSL: ###### RSEL: L#######<br /></td> 
              </tr> 
            </table> 
        </div> 

</div> 

<!--<script>
function getParameterByName(name, url) {
    if (!url) url = window.location.href;
    name = name.replace(/[[]]/g, "$&");
    var regex = new RegExp("[#&]" + name + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/+/g, " "));
}

document.getElementById('pUserID').value = getParameterByName('id');
</script> -->

</body> 
</html> 

这里是 POST 请求的 Wireshark 数据包:

超文本传输​​协议
    POST /live/red_lojson/100eng.json?sh=0&ph=1383&ivh=928&dt=2720&pdt=214&ict=&pct=1&perf=widget%7C214%7C16%2Clojson%7C1027%7C656% 2Csh%7C1031%7C0%2Csh%7C1035%7C16&rndr=render_toolbox%7C1375&cmenu=null&ppd=4&ppl=4&fbe=&xmv=0&xms=0&xmlc=0&jsfw=
        请求方法:POST
        请求 URI [截断]: /live/red_lojson/100eng.json?sh=0&ph=1383&ivh=928&dt=2720&pdt=214&ict=&pct=1&perf=widget%7C214%7C16%2Clojson%7C1027%7C656 %2Csh%7C1031%7C0%2Csh%7C1035%7C16&rndr=render_toolbox%7C1375&cmenu=null&ppd=4&ppl=4&fbe=&xmv=
        请求版本:HTTP/1.1
    主持人:m.addthis.com\r\n
    用户代理:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0\r\n
    接受:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
    接受语言:en-US,en;q=0.5\r\n
    接受编码:gzip,deflate\r\n
    Referer: http://website/\r\n
    内容长度:0\r\n
    内容类型:text/plain;charset=UTF-8\r\n
    饼干:na_tc=Y; uid=597293e6c72cb3be; na_id=2017072123300513069970337317; uvc=27%7C47%2C4%7C48%2C0%7C49%2C10%7C50%2C3%7C51; loc=MDAwMDBPQ0FVTlMxNDYxMzMxMjAwMDAwMDAwVg==;亩= 0; ssc=pinterest%3B1%2Cgoogle%3B1\r\n
    连接:保持活动\r\n
    \r\n

您从 Wireshark 显示的 POST 请求与您显示的 HTML 不匹配。事实上,POST 请求甚至根本不是 HTML 网络表单提交。

您不需要创建自己的 TIdCookieManager 对象,TIdHTTP 可以在内部为您创建一个。而且,就此而言,TIdHTTP 也可以 create the TIdSSLIOHandlerSocketOpenSSL object for you

此外,您正在泄漏 CookieMonsterIdSSL 对象(除非您是 运行 ARC 平台上的此代码)。

无论如何,您没有正确填写 TStringList,甚至没有填写。您必须为 <form> 中具有 name 和非空白 value 的每个 <input> 字段添加一个条目。这包括所有 hidden 字段、脚本分配的字段等。不这样做很容易导致 "Internal Server Error" 失败。您只为 HTML 表单定义的 10 个输入字段中的 1 个提供值。

根据您显示的 HTML,试试这个:

procedure TMSBS_App_GUI.SubmitClick(Sender: TObject);
var
  Params : TStringList;
  IdHttp : TIdHTTP;
  UserID, DOB, Password, AgentName, Response: String;
begin
  IdSSLOpenSSL.LoadOpenSSLLibrary;

  UserID := ...;
  DOB := ...;
  Password := ...;

  IdHttp := TIdHTTP.Create;
  try
    IdHttp.AllowCookies := True;
    IdHttp.ReadTimeout := 30000;
    IdHttp.HandleRedirects := True;
    //IdHttp.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHttp);

    Response := IdHttp.Get('https://' + website);

    // I *suspect* the following value is randomly
    // generated when the HTML is requested. If
    // so, you will have to parse it out each time...
    AgentName := 'boFynyFE9jczy7ra1lzqLmXPeVc9xLptAWQSI9ksks1Hx/oGQmJxQA7Fy25/Xt9X';

    Params := TStringList.Create;
    try
      Params.Add('autherrmsg=Login failed. Please try again.');
      Params.Add('username=' + UserID + DOB);
      Params.Add('url=<ERROR_INFORMATION>');
      Params.Add('proxypath=<PROXY_PATH>');
      Params.Add('pUserID=' + UserID);
      Params.Add('pDOB=' + DOB);
      Params.Add('password=' + Password);
      Params.Add('target=http://website');
      Params.Add('smauthreason=0');
      Params.Add('smagentname=' + AgentName);

      IdHttp.Request.Referer := 'http://' + website;
      Response := IdHttp.Post('https://' + website, Params);
      Memo1.Text := Response;
    finally
      Params.Free;
    end;
  finally
    IdHttp.Free;
  end;
end;