信州大学ACSU多要素認証の自動化
2025年8月09日概要
多要素認証がめんどくさかったので、信州大学ACSUの多要素認証「WisePoint」のイメージ認証を自動化するTampermonkeyユーザースクリプト「wisepoint-auto」を作成しました。
GitHubリポジトリ:UltiMorse/wisepoint-auto
注意:
本スクリプトは検証目的であり、実運用や利用は推奨しません。
セキュリティ上のリスクや規約違反となる可能性があるため、自己責任でご利用ください。

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は存在しない。 - 配置は毎回ランダム。ファイル名からアルファベットを判定
自動化の流れ
- 5×5マスの各divから background-image のファイル名を取得
- ファイル名からアルファベットを判定(Qはスキップ)
- ユーザーが設定した順番(例:A→B→C→D)で該当するマスをクリック
- 4つ選択後、「ログイン」ボタン(input#btnLogin)をクリック
使い方
- Chrome拡張「Tampermonkey」をインストール
- Chrome拡張の開発者モードをオンにし、ユーザースクリプトの実効を許可
- GitHubリポジトリからスクリプトを取得
- Tampermonkeyで新規スクリプトとして追加
- スクリプト内の
const PASSWORD = ["A","B","C","D"];
を自分の設定に編集 - WisePointイメージ認証画面で自動クリックが動作することを確認
- 以上がわからなければ使用はおすすめしません。
スクリプト本体 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やメールでお気軽にどうぞ