C# (WebClient/HttpWebRequest) を使用して WordPress 管理パネルにログインしようとしていました。
に POST リクエストを送信し/wp-login.php
ます。次のような Cookie で応答します。
Set-Cookie: wordpress_26...cf9e; path=/wordpress/wp-content/plugins; httponly
Set-Cookie: wordpress_26...cf9e; path=/wordpress/wp-admin; httponly
Set-Cookie: wordpress_logged_in_26...43b3; path=/wordpress/; httponly
また、次の場所にもリダイレクトされ/wp-admin/
ます。Location: http://109.120.169.99/wordpress/wp-admin/
問題は、2 番目の Cookie (with path=/wp-admin
) が CookieContainer に追加されないため、ログインに失敗することです。
HttpWebRequest ソース コードとhttp://referencesource.microsoft.com/#System/net/System/Net/_CookieModule.cs (OnReceiveHeaders) を調べたところResponseUri
、すべての Cookie に使用されていることがわかりました。
応答 uri を使用して手動で Cookie を追加しようとしたところ、パスが無効であるという例外が発生しました。その後、私はこの答えCookieException with CookieContainer: The 'Path' part of the cookie is invalidを見つけました: CookieContainer に追加するとき、Uri は Cookie からのパスと一致する必要があると書かれています。
結局、AllowAutoRedict を無効にし (手動で処理)、これらの Cookie を手動で追加しました。
これが私のコードです:
public static void Auth(string url, string username, string password)
{
var webClient = new CookieAwareWebClient();
// load page to get some cookies
string loginPageHtml = webClient.DownloadString(url);
webClient.Headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
webClient.Headers.Add(HttpRequestHeader.Referer, url);
var postValues = new NameValueCollection()
{
{"log", username},
{"pwd", password},
{"wp-sumbit", "Log In"},
{"redirect_to", url.Replace("wp-login.php", "wp-admin/") },
{"testcookie", "1"}
};
webClient.AllowRedirects = false; // to handle cookies and redirect manually
byte[] response = webClient.UploadValues(url, postValues);
string html = Encoding.UTF8.GetString(response);
// <FIX>, handle cookies and redirect manually
string cookies = webClient.ResponseHeaders[HttpResponseHeader.SetCookie];
var cookiesArr = cookies.Split(',');
foreach (var cookieStr in cookiesArr)
{
var parts = cookieStr.Split(';').Select(s => s.Trim());
foreach (var part in parts)
{
if (part.Contains("path="))
{
string path = part.Replace("path=", "");
var uri = new Uri(url);
if (path != "/" && !uri.LocalPath.Contains(path))
{
var uriBuilder = new UriBuilder(uri.Scheme, uri.Host, 80,
path);
webClient.CookieContainer.SetCookies(uriBuilder.Uri, cookieStr);
}
}
}
}
// </FIX>
while (webClient.StatusCode() == HttpStatusCode.Redirect)
{
string redirectUrl = webClient.ResponseHeaders[HttpResponseHeader.Location];
html = webClient.DownloadString(redirectUrl);
}
if (html.Contains("?action=logout"))
Console.WriteLine("Login success");
else
Console.WriteLine("Login fail");
}
ウェブクライアント:
// WebClient with CookieContainer
public class CookieAwareWebClient : WebClient
{
private WebRequest _request = null;
private WebResponse _response = null;
public CookieContainer CookieContainer { get; private set; }
public string UserAgent { get; set; }
public bool AllowRedirects { get; set; }
public CookieAwareWebClient()
{
UserAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36";
CookieContainer = new CookieContainer();
AllowRedirects = true;
}
protected override WebRequest GetWebRequest(Uri address)
{
_request = base.GetWebRequest(address);
if (_request is HttpWebRequest)
{
var httpRequest = (HttpWebRequest)_request;
httpRequest.CookieContainer = CookieContainer;
httpRequest.UserAgent = UserAgent;
httpRequest.AllowAutoRedirect = AllowRedirects;
httpRequest.ProtocolVersion = HttpVersion.Version11;
httpRequest.Timeout = 50000;
}
return _request;
}
protected override WebResponse GetWebResponse(WebRequest request)
{
_response = base.GetWebResponse(request);
return _response;
}
// Returns status code of the last response
public HttpStatusCode StatusCode()
{
var httpResponse = _response as HttpWebResponse;
if (httpResponse == null)
throw new InvalidOperationException("Unable to retrieve the status code, maybe you have not made a request yet.");
var result = httpResponse.StatusCode;
return result;
}
}
.NET 4.5 HttpClient も使用してみましたが、同じ結果になりました (下で HttpWebRequest も使用していると思います)。
public static void Auth2(string url, string username, string password)
{
using (var httpClient = new HttpClient())
{
var loginPageHtml = httpClient.GetStringAsync(url).Result;
var postContent = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("log", username),
new KeyValuePair<string, string>("pwd", password),
new KeyValuePair<string, string>("wp-submit", "Log In"),
new KeyValuePair<string, string>("redirect_to", url.Replace("wp-login.php", "wp-admin/")),
new KeyValuePair<string, string>("testcookie", "1")
};
var response = httpClient.PostAsync(url, new FormUrlEncodedContent(postContent)).Result;
string html = response.Content.ReadAsStringAsync().Result;
if (html.Contains("?action=logout"))
Console.WriteLine("Login success");
else
Console.WriteLine("Login fail");
}
}
私は何か間違ったことをしていますか、それとも HttpWebRequest/CookieContainer のバグですか?
誰かがテストしたい場合の便宜のために、完全なソースコードを次に示します: https://gist.github.com/AlexP11223/5c176972426605ee2112 (私のテスト Web サイトとログイン/パスワードも機能するはずです)
そしてフィドラーのログ:
- http://1drv.ms/1qyAgGi — ウェブブラウザ (Chrome)、この Cookie を追加します
- http://1drv.ms/1kqsLDI — Web クライアント
- http://1drv.ms/1kqsC2Y — HttpClient