WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

WebBrowser で HttpOnly 属性の Cookie 取得

by WebSurfer 2012年8月13日 15:35

.NET Framework の WebBrowser を利用した Windows アプリで、ドキュメントに関連付けられている HTTP Cookie を取得するには HtmlDocument.Cookie プロパティ が利用できます。

ただし、HttpOnly 属性を持つ HTTP Cookie は例外です。その理由は、The Code Project のページ Retrieve HttpOnly Session Cookie in WebBrowser に述べられていますが、IE6 以降でクロスサイトスクリプティング対応のため HttpOnly 属性が追加され、その属性を持つ HTTP Cookie にはクライアントスクリプトからアクセスできなくなっているからだそうです。

HttpOnly 属性を持つ HTTP Cookie には、例えば、ASP.NET のセッションクッキー、匿名ユーザー識別用クッキーが該当します(下の Fiddler による応答ヘッダーの画像で、名前が ASP.NET_SessionId と .ASPXANONYMOUS のもの)。

HTTP Cookies(Fiddler による応答ヘッダー)

上の画像の HTTP Cookie の内、HtmlDocument.Cookie プロパティで取得できるのは RandomNumber と DateTimeCookie のみです。この例では、HtmlDocument.Cookie プロパティから取得できる文字列は以下のようになります。

DateTimeCookie=2012/08/13 14:22:48; RandomNumber=1123940529

すべての HTTP Cookie を取得するには、WININET.dll の InternetGetCookieEx メソッド を利用できます(mshtml.dll ではない点に注意)。以下のような感じです。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WebBrowserHttpOnlyCookie
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();

      textBox1.Text = 
          "http://msdntestnew/171-SendCookies.aspx";
    }

    private void button1_Click(object sender, EventArgs e)
    {
      webBrowser1.Navigate(textBox1.Text);
    }

    [DllImport("wininet.dll", CharSet = CharSet.Auto, 
      SetLastError = true)]
    static extern bool InternetGetCookieEx(
        string pchURL, 
        string pchCookieName, 
        StringBuilder pchCookieData, 
        ref uint pcchCookieData, 
        int dwFlags, 
        IntPtr lpReserved);

    const int INTERNET_COOKIE_HTTPONLY = 0x00002000;

    public static string GetCookies(string uri)
    {
      uint datasize = 1024;           
      StringBuilder cookieData = 
        new StringBuilder((int)datasize);
      bool result = InternetGetCookieEx(
                         uri,
                         null,
                         cookieData,
                         ref datasize,
                         INTERNET_COOKIE_HTTPONLY,
                         IntPtr.Zero);

      if (result && cookieData.Length > 0)
      {
        return cookieData.ToString();
      }
      else
      {
        return null;
      }
    }

    private void webBrowser1_DocumentCompleted(
      object sender, 
      WebBrowserDocumentCompletedEventArgs e)
    {
      label1.Text = webBrowser1.Document.Cookie;
      label2.Text = GetCookies(textBox1.Text);
    }
  }
}

Tags: , ,

.NET Framework

クッキーのパス設定

by WebSurfer 2012年1月21日 20:23

クッキーは同名でもパスが違うと別々に保存されるようです。実は、同じ名前を付けると上書きされると誤解していて、先日、半日ぐらいハマってしまいました。(笑)

クッキーについては ASP.NET の Cookie の概要 に詳しく書いてあって、参考にしていましたが、そのあたりのことは書いてないんですよね。(逆に、名前は一意で、同じだと上書きされると書いてあったりします)

クッキーはサーバーからの応答ヘッダーの Set-Cookie: で指定してブラウザに保存させる手段と、クライアントスクリプトの document.cookie で設定する手段があります。

ASP.NET で、サーバー側から応答ヘッダーの Set-Cookie: を使ってクッキーを設定する場合は以下のようにします。

HttpCookie aCookie = new HttpCookie("TestCookie");
aCookie.Value = "Server side";
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

この時、HttpCookie.Path を設定しないと Set-Cookie: に含まれるパス情報は path=/ となります。Path を設定するのは、フォルダまたはアプリケーションに Cookie を制限する場合のみで、普通は設定しないというのが自分の理解です。

クライアントスクリプトでクッキーを設定する場合は document.cookie を利用しますが、このとき path を省略すると、クッキーがブラウザに保存される時そのページのディレクトリがパスに設定されます。

例えば、surferonwww.info/test/abc.aspx というページでクッキーを設定するとパスは /test/ になります(/test ではないところに注意。IE9 の開発者ツールを起動して、[キャッシュ(C)]⇒[Cookie 情報を表示する(I)]で調べられます)。

今回、クライアントスクリプトで設定したクッキーを書き換えまたは削除するため、サーバーから同名のクッキーを応答ヘッダーの Set-Cookie: に設定してやりました。

ハマったのはここのところです。同名だから上書きされると思っていたところ、パスが違うので上書きされず、既存のクッキーはそのままで、Set-Cookie: で送ったクッキーが追加されただけでした。結果、書き換えも削除もできませんでした。

解決策はパスを一致させることです。path=/ とすると、そのドメインのすべてのページ要求でクッキーがサーバーに送られますが、それで問題なければ ASP.NET のデフォルト(?)の path=/ にしておくのがよさそうです。

何故なら、同名でパスが異なるクッキーが複数保存されてしまうと、消去したり内容を書き換えるためクッキーを上書きするには、当該クッキーのパスを指定しなければならず、それは結構大変だからです。また、そういう状態になってしまうと、テスト中にも想定外の動作になって混乱すると思います。

Tags:

ASP.NET

Froms 認証クッキーの永続化

by WebSurfer 2011年12月3日 13:29

ASP.NET 標準の Forms 認証に使用される認証クッキーの「永続化」については、先の記事 永続的ってデフォルトで 30 分? に書きましたが、続けて「永続化」した時としない時の動作の違いを備忘録として書いておきます。

まず、ログインに成功した時にサーバーからブラウザに送られる認証クッキーですが、「永続化」した場合は以下のよう expires によって有効期限が指定されます。「永続化」しない場合は認証クッキーに expires=...; が存在しません。

Set-Cookie: .ASPXAUTH=...; expires=Wed, 30-Nov-2011 13:21:29 GMT; path=/; HttpOnly

expires の日時は、ログインに成功した時点から authentication の forms 要素 の timeout 属性で設定した時間(デフォルトで 30 分)だけ先の日時になります。

認証クッキーは認証チケットの入れ物に過ぎないことに注意してください。認証チケットの有効期限は、ユーザー名などの他の情報と一緒に暗号化されて、認証チケットの中に含まれています。

[永続化」した場合の認証クッキーの有効期限は、認証チケットの有効期限と同じになりますが、認証クッキーの有効期限で認証チケットの有効期限を判定しているわけではありません(そもそも、サーバー側ではクライアントから送られてきたクッキーの有効期限はわかりません)。

認証チケットが発行された以降、有効な認証チケットがブラウザからの要求と一緒にサーバーに送られてくれば、サーバーはユーザーを認証済みと見なし、匿名アクセスが許可されてないページへのアクセスを許可します。

認証チケットが送られてこない場合、もしくは送られてきた認証チケットが期限切れの場合、サーバーはユーザーをログイン画面へリダイレクトします。

slidingExpiration が true になっている場合、timeout 属性で設定した時間の半分を過ぎてアクセスすると、有効期限が延長された認証チケット/クッキーが再発行されます。「永続化」している場合は、expires=...; も延長されて認証クッキーが再発行され、クライアントの HDD 上の既存の認証クッキーが上書きされます。

認証クッキーを「永続化」した場合としない場合(即ち、認証クッキーの有効期限の有無)では、以下のように動きが異なります。

「永続化」した場合

クッキーに有効期限がある(即ち、Set-Cookie: に expires=...; が指定される)ので、ブラウザはクッキーを HDD に保存します。

ブラウザを閉じたり Windows をシャットダウンしたりしても、再度ブラウザを立ち上げてアクセスすれば、ブラウザは認証クッキーを HDD から取得してサーバーに送ります。

クッキーの有効期限が切れると、ユーザーが次にサイトを訪問した時にブラウザによって削除されます。

次回アクセスする際にログイン操作の手間が省けるということが「永続化」のメリットですが、timeout をデフォルトの 30 分程度の短い時間に設定した場合はほとんど意味がないです。

ちなみに、BlogEngine.NET のオリジナルの web.config では timeout="129600"(90 日)に設定してありました。「永続化」もこのぐらい長ければ役に立つと思いますが。

でも、セキュリティ上は、timeout をできるだけ短い時間に、ついでに slidingExpiration は false に設定しておく方がよさそうです。

「永続化」しない場合

この場合、認証クッキーはブラウザのメモリにしか保持されません。従って、ブラウザを閉じれば認証クッキーは消えます。

逆に、ブラウザを閉じなければ認証クッキーは消えません。要求のたびに送られ続けます。(ただし、中身の認証チケットが有効かどうかは別の話で、timeout の設定によります)

Tags: , ,

Authentication

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2017年6月  >>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

View posts in large calendar