玄関の鍵をかけたのに、勝手口から泥棒が?―GraphQL時代の「フィールド単位」の防犯術
こんにちは!セキュリティの最前線で戦っているエンジニアです。
今日は、Web開発の現場で「魔法の杖」のように使われているGraphQLと、そこに潜むちょっと厄介な「泥棒」の話をしようと思います。
「うちは認証をかけているから大丈夫」と思っていませんか?実はそれ、「家の玄関には鍵をかけたけれど、各部屋の扉は開けっ放し」という状態かもしれません。
—
1. XSSとGraphQL:そもそも何が起きているの?
まずは「XSS(クロスサイトスクリプティング)」という泥棒について整理しましょう。これは、悪意のあるプログラム(スクリプト)をあなたのWebサイトに紛れ込ませ、ユーザーのブラウザ上で勝手に動かす手口です。
- 反射型: 泥棒が「これ、見て!」と細工したURLを送りつけ、クリックした瞬間にユーザーの持ち物を奪う。
- 格納型: 掲示板などに「罠」を書き込み、それを見た全員が被害に遭う(これが一番厄介です)。
- DOM型: 画面の表示プログラムの隙を突き、ブラウザの中でこっそり裏工作をする。
GraphQLは、フロントエンドが「必要なデータだけを、必要な分だけ」取れるのが最大の魅力です。しかし、この便利さが仇となり、「認可(権限チェック)」が甘いと、誰でも見放題の「のぞき穴」を作ってしまうリスクがあるのです。
—
2. 「家」で例えるGraphQLの認可
従来のAPI(REST APIなど)は、API全体に鍵をかけるのが一般的でした。「この部屋に入るには、この合鍵が必要ですよ」という仕組みです。
しかし、GraphQLは違います。「家の中の、この引き出しの中身だけは見てもいいけど、隣の引き出しはダメ」という細かい制限が必要です。これを怠ると、悪意のあるユーザーが「あなたの個人情報フィールド」を指定してクエリを送るだけで、いとも簡単に情報を引き抜かれてしまいます。
これを防ぐのが「フィールドレベルの認可」です。
—
3. 実践!ディレクティブを使った賢い防犯対策
コードで例えるのが一番わかりやすいですね。GraphQLには「ディレクティブ」という、関数の手前に置く「門番」のような機能があります。
例えば、ユーザーの「メールアドレス」というフィールドにだけ、厳しい門番を置いてみましょう。
権限がないとアクセスできないことを示すディレクティブ
directive @auth(role: Role) on FIELD_DEFINITION
enum Role {
ADMIN
USER
}
type User {
id: ID!
name: String!
# メールアドレスは本人か管理者しか見られないようにする!
email: String @auth(role: ADMIN)
}
どうやって動くの?(ミドルウェアの考え方)
この @auth というマークを付けると、プログラムが実行される前に「この人は管理者(ADMIN)かな?」とチェックが走ります。
// JavaScriptでの簡易的なイメージ
const resolvers = {
User: {
email: (parent, args, context) => {
// ここでチェック!
if (!context.user || context.user.role !== ‘ADMIN’) {
throw new Error(“ごめんなさい!ここには入れません。”);
}
return parent.email;
},
},
};
このように、「フィールドを読み取ろうとした瞬間に門番が立ちはだかる」仕組みを作っておけば、万が一クエリを改ざんされても、中身を盗まれることはありません。
—
4. XSSを防ぐための「最後の砦」:セキュリティヘッダー
どんなにコードを綺麗に書いても、ブラウザの隙を突かれることはあります。そこで、家の門に「防犯カメラ」と「警備システム」を設置しましょう。それがHTTPレスポンスヘッダーです。
以下の設定をサーバーに入れておくだけで、ブラウザが勝手に「怪しい動き」をブロックしてくれるようになります。
- Content-Security-Policy (CSP): 「このサイト以外の場所からプログラムを読み込んではいけない」とブラウザに強く指示します。泥棒が外部から悪意のあるスクリプトを読み込もうとしても、ブラウザが「ダメ!」と遮断してくれます。
- X-Content-Type-Options: nosniff: ブラウザが勝手に「これプログラムかな?」と推測して実行するのを防ぎます。
—
まとめ:一歩ずつ、安全な場所へ
セキュリティは一度で完璧にはなりません。今日からできることは、以下の3ステップです。
1. 「どこまで公開していいか?」を考え直す: 全てのフィールドに「誰が見ていいか」を定義しましょう。
2. 門番(ディレクティブ)を作る: 重要なフィールドには、必ず認証チェックを通すルールを徹底します。
3. ヘッダーという防犯カメラを設置する: CSPなどを活用し、ブラウザ側で防御を固めます。
「面倒だな」と思うかもしれませんが、これはあなたの大切なユーザーを守るための礼儀です。一つひとつ丁寧に実装していけば、必ず強固な要塞が出来上がりますよ。
何か不安なことがあれば、いつでも相談してくださいね。一歩ずつ、一緒に学んでいきましょう!
コメント