SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

Internet Explorerの認証パスワードとオートコンプリートの操作

Internet Explorerの保存する非公開のデータの表示


  • X ポスト
  • このエントリーをはてなブックマークに追加

Internet Explorerで、認証パスワードの記憶やオートコンプリートを有効にしていると、それぞれのデータがコンピュータに保存されますが、通常これらのデータを表示・操作することはできません。本稿では、これらのデータを一覧するサンプルを作成し、その操作方法を解説します。

  • X ポスト
  • このエントリーをはてなブックマークに追加
サンプルプログラムの実行例(認証パスワードの一覧)
サンプルプログラムの実行例(認証パスワードの一覧)
サンプルプログラムの実行例(オートコンプリートの一覧)
サンプルプログラムの実行例(オートコンプリートの一覧)

はじめに

 Internet Explorerでは、認証パスワードの記憶やオートコンプリートを有効にしていると、それぞれのデータがコンピュータに保存されます。しかし、その内容を操作する方法は、一般には公開されておらず、また、一覧を表示する機能も無いため、ユーザは、現在利用しているコンピュータでどのようなデータが保存されているのかさえ知ることはできません。本稿では、これらのデータを一覧するサンプルを作成し、その操作方法を解説します。

対象読者

  • WindowsでC++を使用してネットワーク関連のプログラムを作成される方。
  • Internet Explorerをサポートするプログラムに興味をお持ちの方。

必要な環境

  • サンプルプログラムは、Windows 98/NT/2000/XPで動作します。
  • サンプルコードは、C++ Builder6およびMicrosoft Visual C++ .NET 2003でコンパイルが可能です。

認証パスワードとオートコンプリート

 Internet Explorerでは、認証(基本認証)が必要なWebサーバにアクセスした時、図1のダイアログを表示して、ユーザーにパスワードの入力を求めます。この時、あらかじめパスワードを記憶するように設定しておけば、以前に入力したユーザー名およびパスワードが自動的に挿入され、[OK]ボタンを押すだけで認証を完了することができます。非常に便利な機能なので利用している方は多いのではないでしょうか。

図1 認証ダイアログ
図1 認証ダイアログ

 また、オートコンプリートが有効になっていれば、フォームで入力が必要になった場合に以前の入力が補完されることもご存じでしょう。どちらも、比較的便利な機能ですが、その設定は、図2のように、利用するか否か、また、記憶されたデータは、全てを削除するか否かの選択しかできません。

図2 オートコンプリートの設定ダイアログ
図2 オートコンプリートの設定ダイアログ

 私は、これらのデータをきめ細かく整理するユーティリティ「HideSeek」を作成する際、データの保存形式や保存方法を調査し、その結果、データは暗号化されレジストリに格納されていることを確認しました。

 そしてこの暗号化されたデータは、Protected Storageというサービスを経由して保存されていました。

Protected Storageとは

 Windowsで起動されているサービスを確認すると、「Protected Storage」というサービスが起動していることを確認できます。サービス一覧の説明では、「秘密キーなどの重要なデータを格納するための保護された記憶域を提供し、許可のないサービス、許可のないプロセス、許可のないユーザーによるアクセスを防ぎます。」と記述されています。

 このサービスは、IE 4.01以降導入されたもので、Windows 2000以降では標準サービスとなっています。IEでは、「認証パスワードの保存」「オートコンプリートの保存」「購読機能」で、このサービスを利用しています。なお、オートコンプリートの設定(図2)で表示される「Webアドレス」についてはIUrlHistoryStg2インターフェースで扱われており、Protected Storageは使用されていません。

 Protected Storageでは、各種のデータを次のレジストリを利用して保存しています。

HKEY_CURRENT_USER\Software\Microsoft\Protected Storage System Provider

 しかし、前述のとおり、その内容は暗号化されておりレジストリエディタなどで編集することはできません。アプリケーションからは、IPStoreインターフェースを利用して、これらのデータの操作することになります。

IPStoreインターフェースの取得

 IPStoreインターフェースは、Protected Storageを利用するための唯一のインターフェースです。このインターフェースは、通常利用されるCOMライブラリのCoCreateInstanceメソッドでは取得することができず、PStoreCreateInstanceメソッドを使用するようになっています。PStoreCreateInstanceメソッドは、「Pstorec.dll」から、LoadLibraryおよびGetProcAddressを使用して関数へのポインタを取得して利用します。

IPStoreインターフェースへのポインタの取得
// 
IPStore *PStore = NULL;

// プロトタイプ宣言
typedef HRESULT (WINAPI *TPStoreCreateInstance)
    (IPStore **, DWORD, DWORD, DWORD);
TPStoreCreateInstance PStoreCreateInstance;

HMODULE hDll = LoadLibrary("pstorec.dll");
if(hDll!=NULL){
    // PStoreCreateInstanceファンクションのポインタを取得
    PStoreCreateInstance = (TPStoreCreateInstance)
        GetProcAddress(hDll,"PStoreCreateInstance");
    if(PStoreCreateInstance!=NULL){
        // IPStoreインターフェースの取得
        if(S_OK != PStoreCreateInstance(&PStore,NULL,NULL,0)) 
            PStore = NULL;// インターフェースの取得に失敗
        }
    }
}

IPStoreのデータ構造とメソッド

 IPStoreのデータは、図3のような形式で保存されています。

図3 IPStoreのデータ構造
図3 IPStoreのデータ構造

 最初の階層である「Type」については、IPStoreインターフェースのEnumTypesメソッドでIEnumPStoreTypesインターフェースを取得し、同インターフェースのNextメソッドを利用して全てを列挙することができます。また、それぞれの「Type」の階層下の「SubType」についても、EnumSubtypesメソッドを使用して同じ要領で取得が可能です。「SubType」の階層下である「ItemName」については、EnumItemsメソッドでIEnumPStoreItemsインターフェースを取得し、同インターフェースのNextメソッドを利用して全てを列挙します。「ItemName」が取得できた後は、それぞれの「Item」をReadItemメソッドを使用して取得する事になります。

 結果的にIPStoreで保存されているデータは、ItemNameLPWSTR形式)およびItemBYTE形式)で1セットとなっています。

 本稿の目的とするIEの認証パスワードや、オートコンプリートのデータは、表1に示したType(GUID形式)およびSubType(GUID形式)で保存されています。従って、あらためてEnumTypesEnumSubtypesTypeSubTypeを列挙する必要はありません。サンプルでは、これらのGUIDを直接指定してIEnumPStoreTypesインターフェースを取得し、ItemNameを列挙することでデータを取得しています。

表1 IPStoreで使用されるGUID
データの種類Type(GUID)SubType(GUID)
認証パスワード5E7E8100-9138-11D1-945A-00C04FC308FF00000000-0000-0000-0000-000000000000
オートコンプリートE161255A-37C3-11D2-BCAA-00c04FD929DBE161255A-37C3-11D2-BCAA-00c04FD929DB

 サンプルプログラムでは、汎用的にTypeおよびSubTypeを与えてデータを取得するTPStore::Initメソッドを使用していますので、詳しくはそちらを参照してください。また、IPStoreについては、MSDNライブラリの『IPStore』にドキュメント化されています。インターフェースやメソッドの詳細については、こちらを確認して下さい。

その他のTypeおよびSubTypeについて
 サンプルでは、認証パスワードおよびオートコンプリートのデータを取得することに専念していますが、先に紹介したEnumTypesおよびEnumSubtypesを使用すると、Protected Storageを使用して格納されているすべてのデータを列挙することができます。
 各種のデータが取得できると思いますので、興味のある方は、ぜひ試してみてください。

Protected Storageの列挙

認証パスワード

 次のコードは、認証パスワードのデータを取得するコードです。

IEの認証パスワードを取得する
GUID AuthGuid = { 0x5E7E8100, 0x9138,0x11D1,
    { 0x94, 0x5A, 0x00, 0xC0, 0x4F, 0xC3, 0x08, 0xFF} };
GUID AuthSubGuid = { 0x00000000, 0x0000, 0x0000,
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} };

IEnumPStoreItems *pItems;

// Type及びSubTypeを指定して、IEnumPStoreItemsを取得する
if(0 == PStore->EnumItems(0, &AuthGuid, &AuthSubGuid,0,&pItems)){
    LPWSTR ItemName;
    DWORD Fetched;
    while(TRUE){
        //IEnumPStoreItemsのNextメソッドで一覧する
        pItems->raw_Next(1,&ItemName,&Fetched);
        if(!Fetched)// FeatchがTRUEの間、次のデータが存在する
                  break;
        DWORD d;
        BYTE *Buffer;
        if(0 == PStore->ReadItem(PST_KEY_CURRENT_USER,
            &Guid,&SubGuid,ItemName,&d,&Buffer,NULL,0)){
            // この時点で、ItemName及びBuffer(d はBufferに
            // 取得したサイズ)にデータが格納されています。

        }
    }
    pItems->Release();// インターフェースの開放
}

 最初に、IPStoreインターフェースのメソッドである、EnumItemsに表1で示したTypeおよびSubTypeであるGUIDを与えて、IEnumPStoreItemsインターフェースを取得します。続いて同インターフェースのメソッドであるNextを利用して階層下のItemNameを列挙します。Nextで列挙するループは、第3パラメータであるFetchedFALSEになった時点で終了です。

 サンプルプログラムの実行画面から分かるとおり、保存されているデータは、「ホスト」「ポート番号」「認証名」「ユーザー」「パスワード」の5種類です。取得した、ItemNameおよびItemには、下記の形式でそれぞれのデータが保存されています。

  • ItemNameLPWSTR
  • ホスト名:ポート番号/認証名
  • ItemBYTE
  • ユーザ@パスワード

 サンプルプログラムでは、CSampleDlg::AuthRefresh(VC++)もしくは、TForm1::AuthRefresh(C++Builder)でこの解釈を行っています。詳しくはそちらを参照してください。

WNetEnumCachedPasswordsについて
 Windows 95/98/Meの場合、IEの認証パスワードのデータは、Protected Storageを使用していません。同OSでは、WNetEnumCachedPasswordsを使用して、認証パスワードを取得できます。
WNetEnumCachedPasswordsの使用例
BOOL CALLBACK pce(PASSWORD_CACHE_ENTRY *x, DWORD)
{
    char ItemName[2048];
    char Item[2048];

    if(x->nType==0x13){ //nTypeが0x13のデータは、IEの認証パスワード
        memmove(Item, x->abResource, x->cbResource);
        ItemName[x->cbResource] = 0;
        memmove(Data, x->abResource+x->cbResource, x->cbPassword);
        Item[x->cbPassword] = 0;
        // この時点で、ItemName及びItemにIPStoreの場合と
        // 同一の形式でデータが取得できる
        // ※ ただし、ItemNameはwchar_t * ではない事にに注意が必要
    }
    return TRUE;
}

// WNetEnumCachedPasswordsへのポインタをmpr.dllから取得する
HMODULE hDll = LoadLibrary("mpr.dll");
if(hDll!=NULL){
    WORD (__stdcall *enp)(LPSTR, WORD, BYTE, void*, DWORD) =
        (WORD (__stdcall *)(LPSTR, WORD, BYTE, void*, DWORD))
        GetProcAddress(hDll, "WNetEnumCachedPasswords");
    if(enp)
        (*enp)(0,0, 0xff, pce, 0);
}
 ※WNetEnumCachedPasswordsについては、既に色々な場所でその使用方法が公開されていますが、マイクロソフトの公式なドキュメントはありません。

オートコンプリートの列挙

 サンプルプログラムの実行画面を見ていただけると分かりますが、現在コンピュータ上で保存されているデータは次の3種類です。

  • フォーム名
  • オートコンプリート文字列
  • 保存年月日

 なお、ここで「フォーム名」とは、テキスト入力を促すフォームを表示する際にHTMLで記述される「<input type=text name=target_name >」のnameに指定された名前(target_name)です。サンプルプログラムの実行画面から一つのフォーム名に対し複数のオートコンプリート文字列が保存されている事が分かると思います。Internet Explorerでは、フォームへの入力途中にこのデータから先頭一致する文字列を表示しています。

 オートコンプリートも認証パスワードの場合と同じ要領で、TypeおよびSubTypeに適切なGUIDを与えるだけでデータの取得が可能です。ただし、こちらのItemNameおよびItemへの格納形式は、少し複雑になっています。

 IEnumPStoreItemsインターフェースのNextで取得できるItemの名前は、オートコンプリートの場合、「フォーム名:StringData」および「フォーム名:StringIndex」の2つの形式になっています。つまり、2つのデータで1つのフォーム名に該当するデータが保存されている事になります。そして、「フォーム名:StringData」には、フォーム名に該当する複数のオートコンプリート文字列が保存されており、「フォーム名:StringIndex」には、その各データの保存されたオフセットおよび登録日付などのデータが保存されています。すなわち、2つのデータを取得した後、StringIndexの情報を元にStringDataを読み出す必要があるのです。

図4 StringIndexデータの一例
図4 StringIndexデータの一例

 図4は、StringIndexのデータの一例です。この例を元に、StringIndexの内部を説明してみましょう。まず最初の4バイトですが、ここでは、「0x57」「0x49」「0x43」「0x4B」となっていますが、これは、キャラクタで「WICK」となり定型の文字列です。次の4バイトは、「0x18」「0x00」「0x00」「0x00」でDWORDのリトルインデアン形式で保存されたヘッダサイズ(「0x00000018」 24バイト)です。そして次の4バイトが「0x02」「0x00」「0x00」「0x00」であり、同じように格納されたデータの個数(「0x00000002」 2個)となっています。取得したヘッダサイズ分だけポインタを進めてみると、一つ一つのデータの情報が16バイトづつ並んでいます。1つのデータの情報は、最初の4バイトがデータのオフセット、次の8バイトが保存の日付です。つまり、例として示したStringIndexでは、StringDataに2つのデータが格納されており、それぞれ、オフセット「0x00」バイト目からと「0x16」バイト目から始まっていることが分かります。なお、StringData内は、ワイド文字形式で格納されておりますので注意してください。

 オートコンプリートのデータ解釈については、CSampleDlg::AutoRefresh(VC++)もしくは、TForm1::AutoRefresh(C++Builder)で実装しております。詳しくはそちらを参照してください。

IPStoreデータの削除・更新

 今回作成したサンプルプログラムは、一覧の列挙しか行っておりません。しかし、IPStoreには、取得以外に削除や追加のメソッドも用意されていますので、これらを利用して同データを自由に変更することが可能です。なお、オートコンプリートのデータについては、1つのフォームに対する複数のデータが1つセットとして管理されていますので、その一部を削除したり編集したりする場合は、いったん全てのデータ取得し、データ構造を再構築して格納する必要があるため、やや注意が必要です。

まとめ

 本稿では、Internet Explorerの保存する非公開のデータについて解説しました。本来は秘匿されているはずのデータを操作する方法ですので、アプリケーション作成の際にこの技術を利用する場合は、セキュリティ上の問題を十分に考慮してください。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

平内 真一(ヒラウチ シンイチ)

 クラスメソッド株式会社 モバイルアプリサービス部所属 仕事では、iOSアプリの開発を行っております。 会社ブログ 個人ブログ...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/147 2007/12/10 13:53

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング