0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Node.js/Express + PostgreSQLでCORS・SQLインジェクション・XSS対策を実装した話【元NWエンジニア視点】

0
Posted at

estimate_invoice_app_architecture.png

はじめに

Node.js/Express + PostgreSQLで開発した見積・請求書管理アプリで、CORS設定・SQLインジェクション対策(パラメータ化クエリ)・XSS対策をどう実装したかをまとめます。ネットワークエンジニア時代の「境界防御」「最小権限」の考え方が、Web開発のセキュリティ実装にもそのまま活きていると感じています。

今回、個人開発した見積・請求書管理アプリ(estimate-invoice-app)で実装したセキュリティ対策について、NWエンジニア時代の視点を交えてまとめます。

アプリ概要

  • アプリ名: estimate-invoice-app(見積・請求書管理アプリ)
  • 技術スタック: React(フロントエンド) / Node.js・Express(バックエンド) / PostgreSQL(Neon)
  • デプロイ: Render(フロントエンド・バックエンドを別サービスとして分離)
  • 想定用途: フリーランス・小規模事業者向けの見積書・請求書作成ツール(シングルユーザー想定)

CORS対策

バックエンドはcorsミドルウェアを使用し、許可するオリジンを環境変数で管理しています。

// server/src/index.js
import cors from 'cors';

app.use(cors({ origin: process.env.CORS_ORIGIN || 'http://localhost:5173' }));

本番環境ではCORS_ORIGINにフロントエンドの本番URL(https://estimate-invoice-frontend.onrender.com)を設定し、開発環境ではローカルURLにフォールバックする構成にしています。

Renderではフロントエンドとバックエンドを別サービスとしてデプロイしているため、この設定がないと本番のフロントからのリクエストがすべて拒否されてしまいます。ネットワークエンジニア時代に「境界で誰を通すか」を常に意識していた感覚が、ここでのオリジン制御にもそのまま活きています。

正直に書いておくと、この設定に気づかずに一時的に環境変数が未設定のまま動かしていた時期があり、意図した制御が効いていない状態になっていました。「設定したつもり」で終わらせず、実際にブラウザの開発者ツール(Networkタブ)でリクエストの挙動を確認することの大切さを痛感しました。

パラメータ化クエリ(SQLインジェクション対策)

データベースアクセスにはpgパッケージを使用し、すべてのクエリでプレースホルダを使ったパラメータ化クエリに統一しています。

// 悪い例(文字列結合、SQLインジェクションのリスクあり)
// const result = await pool.query(`SELECT * FROM invoices WHERE id = ${id}`);

// 実際の実装(プレースホルダを使用)
const result = await pool.query(
  'SELECT * FROM invoices WHERE id = $1',
  [id]
);

ユーザー入力を直接SQL文に埋め込まず、必ずプレースホルダ($1, $2...)経由で渡すことで、悪意のある入力がSQL文として解釈されることを防いでいます。

入力バリデーション・XSS対策

フォーム入力に対するバリデーションに加え、PDF出力時のXSS対策として、HTML生成時にはエスケープ処理を行うesc()関数を用意しています。

// HTMLエスケープ関数の例
function esc(str) {
  return String(str)
    .replace(/&/g, '&')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

見積書・請求書はPuppeteerでHTMLからPDFに変換して出力する仕組みのため、クライアント名や品目名などのユーザー入力がそのままHTMLに埋め込まれる箇所があります。ここでエスケープ処理を怠ると、入力値経由でスクリプトが実行される可能性があるため、出力時に必ずesc()を通す運用にしています。

正直に書いておきたい制約

セキュリティを意識して実装した一方で、現時点でカバーできていない部分も正直に書いておきます。

  • 認証機能は未実装:シングルユーザー利用を前提とした設計になっており、複数ユーザーでのアクセス制御は今後の課題です
  • DB接続のSSL証明書検証は無効化ssl: { rejectUnauthorized: false }):Neonの証明書構成に対応するための現実的な妥協ですが、厳密には証明書検証を有効にする方法も検討の余地があります

「完璧なセキュリティ」を主張するのではなく、何を対策し、何がまだ課題として残っているかを明確にすることも、信頼できるエンジニアであることの一部だと考えています。

まとめ

ネットワークエンジニア時代の「境界防御」「最小権限」といった考え方は、Webアプリ開発における

  • CORSでのオリジン制御
  • パラメータ化クエリでのSQLインジェクション対策
  • 出力時のエスケープ処理によるXSS対策

といった実装にそのまま応用できると感じています。

現在フリーランスとして、React / Node.js / Claude APIを使ったWebアプリ開発の案件を募集しています。セキュリティを意識した実装が必要な案件があれば、ぜひお声がけください。

注意:デモは無料枠のRenderにデプロイしているため、しばらくアクセスがないとサーバーがスリープします。初回アクセス時は起動に30秒〜1分ほどかかる場合がありますので、そのままお待ちいただければ表示されます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?