ショート動画プラットフォームを経由して公開した小程序で予期せぬアクセス集中が発生。リソースパックが短期間で枯渇し、アクセスログ分析により不正リクエストの存在を確認。自然流量と対照実験の結果、名前の選定がアクセスパターンに与える影響を明確に把握。以下、実際の対策手法と課題を技術的視点で解説。
アクセス異常の検出
30時間分のリソースパックが数日で消費されるアラートが発生。アクセスログを調査したところ、特定IPからの連続的な動画テキスト抽出リクエストが確認。通常の利用パターンとは異なるリクエスト間隔とPCクライアントからのアクセスが多数を占めていた。署名検証機構の実装
リクエスト正当性を検証するため、SHA1ベースの署名メカニズムを導入。フロントエンド処理では、リクエストパラメータをソートし静的キーを追加してハッシュ値を生成。バックエンドでは同様のアルゴリズムで検証を実施。
const createRequestHash = (payload) => {
const timestamp = Date.now();
const nonce = Math.floor(100000 + Math.random() * 900000);
payload.ts = timestamp;
payload.nonce = nonce;
payload.apiKey = config.secretKey;
const sortedValues = Object.values(payload).sort();
const rawString = sortedValues.join('');
payload.signature = sha1(rawString);
delete payload.apiKey;
return payload;
};
サーバーサイドではSpring Bootのインターセプタを用いて検証ロジックを実装。タイムスタンプ有効性チェックとパラメータ再計算による署名一致判定を実施。
@Component
public class ApiAuthInterceptor implements HandlerInterceptor {
private static final String SECRET_KEY = "secure_value";
private static final long TIME_WINDOW = 300;
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
try {
String body = IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8);
JSONObject json = JSON.parseObject(body);
String clientSig = json.getString("signature");
long timestamp = json.getLongValue("ts");
if (System.currentTimeMillis() / 1000 - timestamp > TIME_WINDOW) {
sendError(res, "Timestamp expired", 401);
return false;
}
List<String> params = new ArrayList<>();
json.forEach((k, v) -> {
if (!"signature".equals(k) && !"ts".equals(k)) {
params.add(v.toString());
}
});
params.add(SECRET_KEY);
Collections.sort(params);
String serverSig = SecureUtil.sha1(String.join("", params));
if (!clientSig.equals(serverSig)) {
sendError(res, "Invalid signature", 401);
return false;
}
return true;
} catch (Exception e) {
sendError(res, "Request processing failed", 400);
return false;
}
}
}