Reading cookies from Internet Explorer
In order to work around cases where it wasn't possible
automatically authenticate with a website, I wanted the ability
to use an embedded Internet Explorer window for manual
authentication and then reuse the cookies. This article
describes how to read cookies indirectly using
InternetGetCookieEx
or directly from a WebBrowser
control.
Reading cookies
The InternetGetCookieEx
Win32 API can be used to read
cookie name value pairs for a given URI. You can use it to read
all cookies or a named cookie. You can also specify which type
of cookies to include.
BOOLAPI InternetGetCookieExW(
LPCWSTR lpszUrl,
LPCWSTR lpszCookieName,
LPWSTR lpszCookieData,
LPDWORD lpdwSize,
DWORD dwFlags,
LPVOID lpReserved
);
To use this function, we specify the URL we want to query, the
name of the cookie to lookup (or null
for all), a buffer to
store the cookie data, the length of the buffer and then flags
to describe what we want returned. The function returns true
if it succeeds, otherwise false
. In line with other Win32
calls, you can use GetLastError
to get the error code in the
event of a failure.
The last part is quite important as if the buffer provided is
too small, the function will fail and calling GetLastError
will return ERROR_INSUFFICIENT_BUFFER
. In this case, the
buffer length parameter will contain the size required to
contain the data so that you can allocate a new buffer and call
the function again.
I saw a number of naïve implementations on the internet that
called InternetGetCookieEx
without doing any sort of error
checking; this could lead to subtle bugs in the event that the
cookies are longer than the supplied buffer as no data will be
returned.
The following function can be used to get all cookies for a
given URI. I create a buffer that will hold 1024 characters and
make a call to InternetGetCookieEx
. If it fails but the error
code is ERROR_INSUFFICIENT_BUFFER
then I increase the buffer
size and try again.
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool InternetGetCookieEx(string lpszUrl, string lpszCookieName, StringBuilder lpszCookieData, ref int lpdwSize, int dwFlags, IntPtr lpReserved);
const int ERROR_INSUFFICIENT_BUFFER = 122;
const int INTERNET_COOKIE_HTTPONLY = 0x00002000;
public static string GetCookies(string uri)
{
StringBuilder buffer;
string result;
int bufferLength;
int flags;
bufferLength = 1024;
buffer = new StringBuilder(bufferLength);
flags = INTERNET_COOKIE_HTTPONLY;
if (InternetGetCookieEx(uri, null, buffer, ref bufferLength, flags, IntPtr.Zero))
{
result = buffer.ToString();
}
else
{
result = null;
if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
{
buffer.Length = bufferLength;
if (NativeMethods.InternetGetCookieEx(uri, null, buffer, ref bufferLength, flags, IntPtr.Zero))
{
result = buffer.ToString();
}
}
}
return result;
}
In the example above, I'm requesting to include HTTP only cookies. The documentation states: "Do not use this flag if you expose a scriptable interface, because this has security implications. It is imperative that you use this flag only if you can guarantee that you will never expose the cookie to third-party code by way of an extensibility mechanism you provide."
According to the documentation, the lpdwSize
parameter will be
updated to specify the number of bytes required to hold the
value, however, I'm creating a buffer that uses that value in
chars to keep it simple.
Using the WebBrowser control directly
While using InternetGetCookieEx
would be my preferred
approach, if you already have a WebBrowser
control you can
read the cookies directly from the control. The Document
property of a WebBrowser
control returns a HtmlDocument
that
in turn contains the Cookie
property. The format of the
returned data matches that of InternetGetCookieEx
.
There is one important difference between using
InternetGetCookieEx
andHtmlDocument.Cookie
- the later does not include HTTP only cookies. If you require access to all cookies for a given URI, then useInternetGetCookieEx
instead.
What about the cookie properties?
The methods describe above only seem to return the names and values of cookies, excluding properties such as paths and expiry dates. Unfortunately I don't know of a method of accessing these extended properties in modern versions of Windows.
Using the cookies
As Windows and browsers have evolved, there is probably little use for this particular API call. Internet Explorer is obsolete and third party browsers (and Edge) use their own cookie storage mechanisms that this API cannot access. Speaking for myself, the only use I have for this technique is to make custom requests to an internet resource after using an embedded IE window to authenticate with a website.
The following example demonstrates how you could assign cookies
to a HttpWebRequest
. Note that the SetCookies
method of the
CookieContainer
class requires the cookies to be comma
separated, rather than the semi-colon seperated values returned
by the above function.
CookieContainer cookies;
Uri uri;
string cookieData;
uri = new Uri("https://demo.cyotek.com/features/cookies.php");
cookieData = GetCookies(uri);
cookies = new CookieContainer();
cookies.SetCookies(uri, cookieData.Replace("; ", ","));
HttpWebRequest request;
request = WebRequest.CreateHttp(uri);
request.CookieContainer = cookies;
Demonstration program
The attached demonstration program includes a complete sample
for reading cookies either via InternetGetCookieEx
or from a
WebBrowser
control, and is also available from our GitHub
page.
Postscript
I originally wrote this demonstration back in October 2018 but I delayed it while trying to find out a solution to the missing properties. I was also curious as to why Edge didn't seem to use the same system and had a vague idea looking into that. Then came the news that Microsoft have decided to drop their custom Edge engine and use Chromium instead.
This seems astonishingly short sighted to me and is a real disappointment.
While I confess I don't use Edge as my primary browser (Firefox
is my daily driver due to its extension support and excellent
developer tools), I found it to be a decent browser which didn't
let me down when I did use it. To me it is perplexing that they
are essentially ceding control to Google. It's like Windows
Phone all over again and I like my Windows Phone. It is also
frustrating as currently you can get a reasonable browsing
experience by embedding Internet Explorer via the WebBrowser
control into your application (with a little help) without
having to worry about shipping any dependencies and I had hopes
of doing the same with Edge for a truly modern experience.
Update History
- 2019-01-20 - First published
- 2020-11-22 - Updated formatting
Downloads
Filename | Description | Version | Release Date | |
---|---|---|---|---|
InternetGetCookieExDemo.zip
|
Sample project for the reading cookies from Internet Explorer blog post |
20/01/2019 | Download |
Leave a Comment
While we appreciate comments from our users, please follow our posting guidelines. Have you tried the Cyotek Forums for support from Cyotek and the community?
Comments
Chetan
#
How to do same with Microsoft Edge browser instead of IE?