はじめに
モダンなWebサービスでは、フロントエンドとバックエンドをAPIで接続する構成が一般的になっています。
React、Next.js、VueなどのフロントエンドからAPIを呼び出し、ユーザー情報やコンテンツデータを取得する設計は非常に便利ですが、その一方でAPIが攻撃対象になりやすいという課題もあります。
特に注意したいのが、
- 不正アクセス
- 認証トークンの悪用
- 大量リクエスト
- 不正な入力データ
- 権限チェック不足
です。
本記事では、Webサービス開発で押さえておきたいAPIセキュリティの基本を、JWT、Rate Limiting、入力検証を中心に紹介します。
KRCLUBでも、デジタルサービスの安定運用を考えるうえで、API設計とセキュリティは重要な技術テーマのひとつです。
1. なぜAPIセキュリティが重要なのか
Webサービスでは、多くの処理がAPI経由で実行されています。
例えば、
Browser
↓
Frontend
↓
API
↓
Database
という構造の場合、APIはユーザーとデータベースをつなぐ重要なポイントになります。
APIに十分なセキュリティ対策がないと、不正ユーザーが直接APIへアクセスし、本来許可されていない操作を試す可能性があります。
そのため、フロントエンド側だけでアクセス制御を行うのではなく、バックエンド側でも必ず検証する必要があります。
2. JWTを使った認証
Web APIでは、JWT(JSON Web Token)が認証に使われることがあります。
一般的な流れは以下です。
Login
↓
Authentication
↓
JWT発行
↓
Client保存
↓
API Request + JWT
Node.jsでは、例えば以下のようにトークンを生成できます。
import jwt from "jsonwebtoken";
const token = jwt.sign(
{
userId: 123
},
process.env.JWT_SECRET,
{
expiresIn: "1h"
}
);
API側ではトークンを検証します。
const decoded = jwt.verify(
token,
process.env.JWT_SECRET
);
3. JWT利用時の注意点
JWTを導入するだけで安全になるわけではありません。
重要なのは運用方法です。
有効期限を設定する
長期間有効なトークンは、漏洩した場合のリスクが高くなります。
そのため、
expiresIn: "1h"
のように適切な期限を設定します。
秘密鍵をコードに書かない
以下のような実装は避けるべきです。
const secret = "my-secret-key";
代わりに環境変数を利用します。
process.env.JWT_SECRET
Gitリポジトリへ秘密情報を含めないことも重要です。
4. 認証と認可は別に考える
API設計では、AuthenticationとAuthorizationを区別する必要があります。
Authentication
「誰なのか」を確認する処理です。
例:
- ログイン
- JWT検証
- セッション確認
Authorization
「何をしてよいか」を確認する処理です。
例えば、
一般ユーザー → 記事閲覧
編集者 → 記事編集
管理者 → ユーザー管理
というように、ユーザーごとに権限を分けます。
API側では、ログイン状態だけではなく、操作権限も確認する必要があります。
5. Rate Limitingで大量アクセスを制御する
公開APIでは、短時間に大量のリクエストが送信される可能性があります。
例えば通常のユーザーが1分間に数回しか呼び出さないAPIへ、数千回のリクエストが届いた場合、サーバー負荷が急増します。
そこで利用されるのがRate Limitingです。
Expressでは、例えば以下のように実装できます。
import rateLimit from "express-rate-limit";
const limiter = rateLimit({
windowMs: 60 * 1000,
max: 100
});
app.use("/api", limiter);
この例では、一定時間内のリクエスト数を制限しています。
6. Rate LimitはAPIごとに考える
すべてのAPIに同じ制限を設定する必要はありません。
例えば、
| API | 制限の考え方 |
|---|---|
| Login | 厳しく設定 |
| Search | 中程度 |
| Public Content | 比較的緩く設定 |
| Password Reset | 厳しく設定 |
特にログインやパスワード関連APIは、不正試行対策として制限を厳しくすることが重要です。
7. 入力データを必ず検証する
ユーザーから送信されるデータをそのまま信用してはいけません。
例えば以下のリクエストがあるとします。
{
"username": "sample_user",
"age": 24
}
バックエンドでは、
- usernameは文字列か
- 長さは適切か
- ageは数値か
- 想定範囲内か
を確認します。
Zodを利用する例:
import { z } from "zod";
const schema = z.object({
username: z.string().min(3).max(30),
age: z.number().min(1).max(120)
});
schema.parse(data);
入力検証によって、不正なデータが内部処理へ流れることを防ぎやすくなります。
8. SQL Injectionを防ぐ
データベース操作でも入力値を直接SQLへ埋め込むのは危険です。
例えば、
const query =
"SELECT * FROM users WHERE name = '" +
username +
"'";
のようなコードは避けるべきです。
代わりに、パラメータ化クエリを利用します。
const result = await db.query(
"SELECT * FROM users WHERE name = $1",
[username]
);
ORMを利用する場合でも、入力値の扱いを理解しておくことが重要です。
9. エラーメッセージにも注意する
APIエラー時に内部情報をそのまま返すと、攻撃者へヒントを与える可能性があります。
例えば、
Database connection failed:
postgres://admin:password@example...
のような情報はレスポンスへ含めるべきではありません。
ユーザーには、
{
"error": "Internal Server Error"
}
のような一般的なメッセージを返し、詳細情報はサーバーログ側で管理します。
10. ログと監視を組み合わせる
セキュリティでは「攻撃を完全に防ぐ」だけではなく、「異常を早く発見する」ことも重要です。
監視したい項目:
- ログイン失敗回数
- APIアクセス数
- 403エラー
- 429エラー
- 異常なIPアクセス
- 急激なトラフィック増加
例えば、
API Request
↓
Authentication
↓
Rate Limit
↓
Validation
↓
Application
↓
Logging
のように、複数の防御レイヤーを組み合わせる設計が有効です。
11. KRCLUBが注目する安全なWebサービス設計
KRCLUBでは、Webサービスを長期的に運営するうえで、機能追加だけでなく安全性と安定性の両立が重要だと考えています。
特に注目しているポイントは、
- API認証
- 権限管理
- 不正アクセス対策
- 入力検証
- ログ監視
- インフラの安定性
です。
Webサービスは利用者が増えるほど、APIへのアクセス量や扱うデータも増加します。
そのため、開発初期の段階からセキュリティを設計へ組み込むことが重要になります。
12. セキュリティは一度設定して終わりではない
Webサービスのセキュリティ対策は継続的な改善が必要です。
例えば、
- ライブラリ更新
- 脆弱性チェック
- アクセスログ確認
- 権限設定レビュー
- API仕様変更時の再検証
などを定期的に行います。
特に依存ライブラリが多いJavaScriptプロジェクトでは、パッケージの更新状況にも注意が必要です。
まとめ
安全なWeb APIを構築するためには、単一の対策だけでは十分ではありません。
重要なポイントは以下です。
- JWTを適切に管理する
- 認証と認可を分ける
- Rate Limitingを設定する
- 入力値を検証する
- 安全なデータベース操作を行う
- エラー情報を必要以上に公開しない
- ログと監視を活用する
KRCLUB playでも、ユーザー体験だけでなく、安全性と安定性を含めたWebサービス設計に注目しています。
高機能なサービスを作ることと同じくらい、「安全に使い続けられる仕組み」を設計することが、これからのWeb開発では重要になるでしょう。