UltiMorseのブログ

信州大学ACSU多要素認証の自動化

概要

多要素認証がめんどくさかったので、信州大学ACSUの多要素認証「WisePoint」のイメージ認証を自動化するTampermonkeyユーザースクリプト「wisepoint-auto」を作成しました。
GitHubリポジトリ:UltiMorse/wisepoint-auto

注意:
本スクリプトは検証目的であり、実運用や利用は推奨しません。
セキュリティ上のリスクや規約違反となる可能性があるため、自己責任でご利用ください。
wisepointの画面
自動入力ボタンで一発ログイン

WisePointイメージ認証の仕様

  • 5×5のマス(合計25マス)が画面上に表示される
  • 各マスは <div class="input_imgdiv_class" id="buttonN" ...>(N=0〜24)で表現
  • 画像は style="background-image:url('/idp/tenant/0/images/imatrix/iXX.gif')" で指定
  • 画像ファイル名とアルファベットの対応(Qは存在しない)
    例:i1.gif=A, i2.gif=B, ... i16.gif=P, i17.gif=R, ... i25.gif=Z
    ※Qは存在しない。
  • 配置は毎回ランダム。ファイル名からアルファベットを判定

自動化の流れ

  1. 5×5マスの各divから background-image のファイル名を取得
  2. ファイル名からアルファベットを判定(Qはスキップ)
  3. ユーザーが設定した順番(例:A→B→C→D)で該当するマスをクリック
  4. 4つ選択後、「ログイン」ボタン(input#btnLogin)をクリック

使い方

  1. Chrome拡張「Tampermonkey」をインストール
  2. Chrome拡張の開発者モードをオンにし、ユーザースクリプトの実効を許可
  3. GitHubリポジトリからスクリプトを取得
  4. Tampermonkeyで新規スクリプトとして追加
  5. スクリプト内の const PASSWORD = ["A","B","C","D"]; を自分の設定に編集
  6. WisePointイメージ認証画面で自動クリックが動作することを確認
  7. 以上がわからなければ使用はおすすめしません。

スクリプト本体 8月11日変更ver.(アクセス権限とdescriptionの修正)

// ==UserScript==
// @name         wisepoint-auto
// @namespace    https://github.com/UltiMorse/wisepoint-auto
// @version      2025-08-11
// @description  ACSUのマトリクス認証自動入力ボタン追加
// @author       UltiMorse
// @match        https://gakunin.ealps.shinshu-u.ac.jp/idp/Authn/External?conversation=*
// @grant        none
// ==/UserScript==


(function() {
    'use strict';

    // 設定:自動クリックするパスワード(順番にクリックされるアルファベット)
    const PASSWORD = ['', '', '', '']; // ['A', 'B', 'C', 'D']; のようにパスワードを設定

    // --- UI ---
    // 自動入力ボタンをログインボタンの左隣に追加
    function addAutoButton() {
        if (document.getElementById('autoInputBtn')) return;
        const btn = document.createElement('button');
        btn.id = 'autoInputBtn';
        btn.textContent = '自動入力';
        btn.type = 'button';
        btn.className = 'form-matrix-button form-button';
        btn.onclick = autoInput;
        const loginBtn = document.getElementById('btnLogin');
        if (loginBtn) {
            btn.style.height = loginBtn.offsetHeight + 'px';
            btn.style.fontSize = window.getComputedStyle(loginBtn).fontSize;
            btn.style.padding = window.getComputedStyle(loginBtn).padding;
            btn.style.marginRight = '8px';
        }
        if (loginBtn && loginBtn.parentNode) {
            loginBtn.parentNode.insertBefore(btn, loginBtn);
        } else {
            document.body.appendChild(btn);
        }
    }

    // --- クリック ---
    // i1.gif→A, i2.gif→B,...i16.gif→P, i17.gif→R,...i25.gif→Z(Qなし)で判定しクリック
    function clickMatrixChar(char) {
        const letters = [];
        for (let i = 0, code = 65; i < 25; i++, code++) {
            if (String.fromCharCode(code) === 'Q') code++;
            letters.push(String.fromCharCode(code));
        }
        const btns = document.querySelectorAll('.input_imgdiv_class');
        for (let btn of btns) {
            const bg = btn.style.backgroundImage;
            const m = bg.match(/i(\d{1,2})\.gif/i);
            if (m) {
                const idx = parseInt(m[1], 10) - 1;
                if (letters[idx] === char) {
                    btn.click();
                    return true;
                }
            }
        }
        return false;
    }

    // --- 自動入力 ---
    async function autoInput() {
        for (const c of PASSWORD) {
            const ok = clickMatrixChar(c);
            if (!ok) {
                alert('エラー');
                return;
            }
            await new Promise(r => setTimeout(r, 200));
        }
        const loginBtn = document.getElementById('btnLogin');
        if (loginBtn) {
            setTimeout(() => loginBtn.click(), 200);
        }
    }

    // --- マトリクス出現監視 ---
    function waitAndAddButton() {
        if (document.getElementById('autoInputBtn')) return;
        const matrix = document.querySelector('[id^="button0"].input_imgdiv_class');
        if (matrix) {
            addAutoButton();
            return;
        }
        const observer = new MutationObserver(() => {
            const m = document.querySelector('[id^="button0"].input_imgdiv_class');
            if (m) {
                addAutoButton();
                observer.disconnect();
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    // --- 初期化 ---
    if (document.readyState === 'loading') {
        window.addEventListener('DOMContentLoaded', waitAndAddButton);
    } else {
        waitAndAddButton();
    }

})();

補足・注意事項

  • WisePointの仕様変更等で動作しなくなる場合があります
  • 不正利用・規約違反等の責任は一切負いません
  • ご指摘・ご意見はXやメールでお気軽にどうぞ

関連記事:Google Search ConsoleでURL毎にインデックス申請