ホスト型API · 無料枠あり

攻撃してくるのは、もうプログラムです。

Proofwire は、どのセッションが攻撃なのかを推測ではなく確定として判別し、 同じリクエストの中で拒否します。正規の利用者に当たることはありません。 反応するのが、正規の利用者には原理的に出せない痕跡だからです。

月10万イベントまで無料カード登録不要障害時は素通し
server.js
// 1. クライアントを1ファイル取得
//    curl -o proofwire.js \
//      https://layersecurity.jp/proofwire/express.js

// 2. 2行(鍵は PROOFWIRE_KEY から読む)
const proofwire = require("./proofwire")({ block: true });

app.use(proofwire.express());

2025年11月、Anthropic は国家支援型のスパイ活動を遮断したと公表しました。そこでは Claude Code が偵察から侵入、横展開、データの持ち出しまで、侵入作業の80〜90%を自律的に実行していました。標的は約30組織 (incident 1263)。 この作戦を止められたのは、商用APIの上で動いていて、提供元がアカウントを消せたからです。 いまやフロンティア級のモデルは重みごと無料で配布されているので、次はそうはいきません。 攻撃の「調べ上げる」工程は、あらゆるサイトに向け続けても割に合う値段まで下がりました。

そして、これは「自動化されているかどうか」では見分けられません。正規のトラフィックの 半分はすでに自動化されていますし、顧客が自社のエージェントから叩いてくるリクエストは、 攻撃してくるものと区別がつきません。同じライブラリ、同じヘッダ、同じ速度です。

導入

1ファイル、依存なし。PROOFWIRE_KEY を環境変数に置き、 ボディパーサーの後ろに差し込むだけです。判定・スコアリング・相関はすべてAPI側で行います。

curl -o proofwire.js https://layersecurity.jp/proofwire/express.js
const proofwire = require("./proofwire")({ block: true });
app.use(proofwire.express());

block: true を外せば、遮断せず通知だけになります。Node 以外なら、Cloudflare Worker 版が同じ働きをします。 PHP でも Rails でも Java でも静的サイトでも、アプリ本体に一切手を入れずに前段へ置けます。

アカウントを作ると鍵が発行されます

月10万イベントまで無料。カード登録は不要です。コンソールでは、鍵・使用量・ 確定したセッションの一覧が見られます。

ターミナルだけで済ませることもできます。あとからログインするためのパスワードが、 このとき一度だけ返ります。

curl -sX POST https://api.layersecurity.jp/v1/signup \
  -H 'content-type: application/json' -d '{"email":"you@company.com"}'

{"key":"pw_…","password":"…","console":"https://layersecurity.jp/proofwire/app/"}

実際の見え方

同じアプリ上の、2つのセッションです。

21:04:11 GET / 200 sess=4f2a think=1.4s 21:04:19 GET /pricing 200 sess=4f2a think=8.2s 21:04:41 GET /login 200 sess=4f2a think=22s └─ 人間。このセッションからは、何も出力されない。 21:07:02 GET / 200 sess=e6dd think=— 21:07:02 GET /.env 404 sess=e6dd think=8ms 21:07:02 GET /admin 404 sess=e6dd think=6ms 21:07:03 GET /api/v1/users 200 sess=e6dd think=4ms 21:07:03 GET /backup.sql 404 sess=e6dd think=5ms … 9秒で25パス ⚠ likely 0.60 breadth=25_paths fuzz_404=11 no_think_time(4ms) └─ 疑わしい。だが確定ではない。ここでは遮断しない。 21:07:19 GET /api/keys?k=AKIAC5192241… 403 sess=e6dd 🚨 confirmed 0.95 decoy_hit x1 + breadth=25_paths └─ 拒否。以降30分、e6dd からの全リクエストを拒否。

この鍵は、どこにも公開していません。リンクもされていないし、robots.txt にも、 リポジトリにも存在しません。存在した場所はただ一つ、17秒前にこのセッションへ返したページの中だけです。 正規の利用者も、正規のクローラーも、正規の連携先も、このリクエストを出す経路がありません。 これは推測ではなく自白であり、自白に誤検知率はありません。

既存の判定が効かない理由

判定材料なぜ効かないか
リクエスト頻度人間が更新を連打すれば10秒で15回叩きます。逆にエージェント側が 4秒に1回へ落とせば、それだけで閾値の下に隠れます。
User-Agent、ヘッドレス判定設定1行で変わります。まともなツールはすでに全部詐称しています。
「自動化されて見える」顧客が使っているエージェントも自動化されて見えます。ここで遮断すると、 失うのは取引先です。
ハニーポット、偽の /admin存在を広告してしまっています。能力の高いエージェントは、アプリ本体と 表面が食い違うことに気づいて迂回しますし、しかも「見張られている」と知ります。
WAFのシグネチャすでに誰かが見たペイロードに対して書かれたものです。モデルは、 ルールが書かれていないリクエストをその場で組み立てます。
こちらからしか渡りようのない値を使った正規のクライアントには、原理的に、一度も、出せません。

仕組み

  • 何も広告しません。 目印も、偽のルートも、追加のホスト名も、バナーも置きません。 スキャナから見た表面はオリジンとバイト単位で同一で、404もアプリ本体が返す本物です。 気づく手がかりが無いので、迂回のしようもありません。
  • 値は、もともと返しているレスポンスの中に紛れます。AWSキー、本番APIキー、データベースURLの形をした値を、HTMLコメントとJSONフィールドに 1つずつ置きます。値はあなたの鍵からHMACで導出するので、こちらは保存していませんし、 顧客どうしで同じ値になることもありません。
  • 見ているのは「使ったかどうか」で、「らしさ」ではありません。速い、スクリプトから来ている、ヘッドレスである — こうした性質は、 どれだけ揃っても確定にはなりません。確定できるのは、値を使ったときだけです。
  • 遮断はローカルで即座に決まります。 クライアントは値を手元に持っているので、 その値を含むリクエストはその場で拒否できます。自白から遮断までの隙間がありません。
  • 行動シグナル(連射・探索の広さ・思考時間ゼロ・404の総当たり)はスコアを上げますが、 単独では likely 止まりで、遮断はしません。 しかも「10パス以上を舐めている」ことを前提条件にしています。 人間が更新を連打すると、それ以外はほぼ全部立ってしまうからです。
  • 壊れたら素通しします。 APIが落ちても、枠を使い切っても、設定が取れなくても、 レスポンスはそのまま出ます。計測はレスポンスを返した後ろでまとめて送るので、 応答時間に乗りません。

できないこと

  • 人間らしく振る舞う、辛抱強い人間の攻撃者は止められません。これは割り切りです。 その代わり、通知を開いても選別する対象がありません。
  • WAFではありませんし、置き換えるものでもありません。block を切れば、 何も遮断しません。
  • 遮断すると、相手は捕捉されたことに気づきます。泳がせたい場合は通知のみで運用してください。 検知の内容は同じです。
  • 値がどこまで届くかで精度が決まります。資格情報の形をしたものが攻撃者の手元に 一切渡らないなら、自白のしようがありません。
  • クライアントは text/htmlapplication/json のボディを 書き換えます(コメント1つ、_k フィールド1つ)。 レスポンスに署名やハッシュを付けている場合は、対象ルートを絞ってください。
  • セッションの識別は IP + User-Agent です。共有NATの内側では粗くなります。 ただし自白そのものは、セッション識別の精度に依存しません。
  • v0です。APIの形は変わります。第三者によるセキュリティレビューはまだ受けていません。

他の言語から使う

HTTPで2回。プロトコルはこれで全部です。

# 何を仕込むか(キャッシュして数時間ごとに更新)
GET  https://api.layersecurity.jp/v1/config   Authorization: Bearer pw_…
  -> { "bait": [ { "id":"aws", "placement":"html-comment", "value":"AKIA…" }, … ] }

# 何が起きたか(最大200件まとめて、投げっぱなし)
POST https://api.layersecurity.jp/v1/events   Authorization: Bearer pw_…
  { "events": [ { "session":"e6dd", "path":"/api/keys", "status":403,
                  "dt":4, "bait":"AKIA…" } ] }
  -> 202 { "alerts": [ { "level":"confirmed", "score":0.95,
                         "evidence":["decoy_hit x1","breadth=25_paths"] } ] }

bait には、受信リクエストの中に見つけた値を入れます。無ければ null です。

料金

無料月10万イベントまで。機能制限なし。カード登録不要。
超えたら429 とリセット日を返します。何も壊れません (クライアントは素通しを続けます)。連絡をもらえれば枠を上げます。
保存するものパス、ステータス、User-Agent、ハッシュ化したセッションID、 そして仕込んだ値が現れたかどうか。リクエストボディも、レスポンスボディも、 ヘッダも保存しません。
開発は株式会社Layer Security(東京)。主力はシャドーAI検知で、 Proofwire は「攻撃してくるのがプログラムなら、攻撃面はどうあるべきか」を 考えたところから生まれました。指摘は歓迎です — contact@layersecurity.jp