🔐

安全なWebサイトを作ろう③

2025/02/21に公開

ごあいさつ

こんにちは!
オアシステクノロジーズの古本です。

今日はWebアプリケーションを構築する上でめちゃめちゃ重要な「セキュリティ」に関する話題です。

この記事の続きです
https://zenn.dev/oasys/articles/80c8878f3bdae3

Webアプリのセキュリティリスクと対策

1. CSRF(クロスサイトリクエストフォージェリ)

攻撃の概要

CSRF(Cross-Site Request Forgery)は、ユーザーが意図しないリクエストを攻撃者が送信させる攻撃手法です。これにより、ユーザーが意図しないアクション(例: パスワード変更、送金処理など)を実行させられる可能性があります。

発生する問題・具体例

  • 不正送金: オンラインバンキングでCSRF攻撃を受けると、ユーザーが知らない間に攻撃者の口座へ送金される。
  • アカウント乗っ取り: SNSやECサイトでメールアドレスやパスワード変更をされると、アカウントを奪われる可能性がある。

攻撃例

<!-- 攻撃者のサイト -->
<img src="https://example.com/api/transfer?amount=10000&to=attacker" />

対策

Next.js の API で cookies を使う場合、SameSite 属性を適切に設定することでCSRF攻撃を防ぐことができます。

import { NextRequest, NextResponse } from 'next/server';

export function middleware(req: NextRequest) {
  const res = NextResponse.next();
  res.cookies.set('session', 'value', { httpOnly: true, secure: true, sameSite: 'Strict' });
  return res;
}

2. CSRFトークンの利用(Spring Boot)

Spring Securityを使うと、CSRFトークンがデフォルトで有効になっています。

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .formLogin(Customizer.withDefaults());
        return http.build();
    }
}

3. Next.jsのミドルウェアを利用した対策

Next.js ではミドルウェアを活用して Content-Security-PolicyPermissions-Policy を適用し、攻撃リスクを低減できます。

import { NextRequest, NextResponse } from 'next/server';

export function middleware(req: NextRequest) {
  const res = NextResponse.next();
  res.headers.set(
    "Content-Security-Policy",
    "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'"
  );
  return res;
}

2. HTTPヘッダインジェクション

攻撃の概要

HTTPヘッダインジェクションは、適切にエスケープされていないユーザー入力をHTTPレスポンスヘッダに組み込むことで、リダイレクトやXSS攻撃を引き起こす手法です。

発生する問題・具体例

  • フィッシングサイトへの誘導: リダイレクトURLを書き換えられ、攻撃者が用意した偽サイトに誘導される。
  • Cookieの漏洩: HTTPヘッダを改ざんすることで、セッションCookieを盗み取られる可能性がある。

攻撃例

// Spring Boot(脆弱な実装例)
@GetMapping("/redirect")
public ResponseEntity<Void> redirect(@RequestParam String url) {
    HttpHeaders headers = new HttpHeaders();
    headers.set("Location", url); // 悪意のあるURLを挿入可能
    return new ResponseEntity<>(headers, HttpStatus.FOUND);
}

対策

1. ユーザー入力のバリデーション

@GetMapping("/redirect")
public ResponseEntity<Void> safeRedirect(@RequestParam String url) {
    if (!url.startsWith("https://example.com")) {
        return ResponseEntity.badRequest().build();
    }
    HttpHeaders headers = new HttpHeaders();
    headers.set("Location", url);
    return new ResponseEntity<>(headers, HttpStatus.FOUND);
}

2. Next.jsのリダイレクト制限

Next.js では next.config.js でリダイレクトルールを制限できます。

module.exports = {
  redirects: async () => [
    {
      source: '/old-route',
      destination: '/new-route',
      permanent: true,
    },
  ],
};

3. メールヘッダインジェクション

攻撃の概要

メールヘッダインジェクションは、メール送信時に適切にサニタイズされていない入力を使用することで、攻撃者がCCやBCCに別のメールアドレスを追加し、スパム送信や情報漏洩を引き起こす攻撃です。

発生する問題・具体例

  • スパムメールの大量送信: メールヘッダを改ざんすることで、企業のサーバーをスパム送信に悪用される。
  • フィッシング詐欺: 送信元を偽装し、信頼できる企業を装った詐欺メールを送ることが可能。

攻撃例

// Spring Boot(脆弱な実装例)
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, false);
helper.setTo(userInput); // ユーザー入力をそのまま使用
helper.setSubject("Your Order Confirmation");
helper.setText("Thank you for your order!");
mailSender.send(message);

対策

1. 入力値のバリデーション

private boolean isValidEmail(String email) {
    return email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
}

参考:
https://zenn.dev/oasys/articles/ca1b07ddd2f633

まとめ

残すところあと1回です!
飽きずについてきてください!w

Discussion