OWASP トップ 10 2025: Web 開発者のための実践ガイド
Web アプリケーションのセキュリティは「あればいいもの」ではなく、責任が伴います。 すべての開発者にとって基本的なものです。そこには OWASP トップ 10 そしてのリスト Open Web Application によって管理される Web アプリケーションの最も重大な脆弱性 セキュリティプロジェクト。このガイドでは、次の方法で各脆弱性を分析します。 コード例 コンクリート 特に Angular と Node.js に重点を置いた実践的なソリューションを紹介します。
OWASP リストは実際の安全データに基づいて定期的に更新されます 世界中で収集されています。最後の公式レビューは 2021 年に遡りますが、コミュニティは は、2025 年に向けて最新情報と最新のガイダンスを提供し続けます。この記事の内容 10 のカテゴリーすべてをカバーし、それぞれの脆弱性と安全なコードを提供します。
前提条件
このガイドは、TypeScript、Node.js、Angular の基本的な知識を前提としています。 それぞれの脆弱性について、脆弱なコードの例と説明が表示されます。 リスクと安全なバージョンの両方。この概念はあらゆる Web スタックに適用されます。
A01:2021 - 壊れたアクセス制御
Il 壊れたアクセス制御 はリストの一番上にあり、 より広範囲にわたる脆弱性。ユーザーがリソースにアクセスできる場合、または 権限のないアクションを実行すること。他の人のデータへのアクセスが含まれます。 ユーザー、権限の昇格、ロール制御のバイパス。
脆弱性の例
// VULNERABILE: chiunque può accedere ai dati di qualsiasi utente
app.get('/api/users/:id/profile', async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user); // Nessun controllo se l'utente autenticato ha diritto di vedere questo profilo
});
// VULNERABILE: IDOR (Insecure Direct Object Reference)
app.get('/api/orders/:orderId', async (req, res) => {
const order = await Order.findById(req.params.orderId);
res.json(order); // L'utente può vedere gli ordini di chiunque cambiando l'ID
});
セーフバージョン
// SICURO: verifica che l'utente acceda solo ai propri dati
app.get('/api/users/:id/profile', authenticate, async (req, res) => {
if (req.user.id !== req.params.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Accesso non autorizzato' });
}
const user = await User.findById(req.params.id);
res.json(user);
});
// SICURO: filtra per utente autenticato
app.get('/api/orders/:orderId', authenticate, async (req, res) => {
const order = await Order.findOne({
_id: req.params.orderId,
userId: req.user.id // Solo gli ordini dell'utente autenticato
});
if (!order) {
return res.status(404).json({ error: 'Ordine non trovato' });
}
res.json(order);
});
Angular のベスト プラクティス
- 埋め込む ルートガード 機密ページを保護するため
- を使用します。 HTTP インターセプター 各リクエストとともに JWT トークンを送信する
- クライアントを決して信頼しないでください。制御はサーバー側でも行われる必要があります。
- 権限に基づいて UI 要素を非表示にしますが、常にバックエンド側で検証します
A02:2021 - 暗号化の失敗
I 暗号化の失敗 データ保護が不十分であることを懸念する 機密情報: 平文のパスワード、古い暗号化アルゴリズム、ハードコードされたキー、 HTTPS を使用しないデータ送信。
脆弱性の例
// VULNERABILE: password salvata in chiaro
app.post('/api/register', async (req, res) => {
const user = new User({
email: req.body.email,
password: req.body.password // MAI salvare in chiaro!
});
await user.save();
});
// VULNERABILE: algoritmo di hash debole
import crypto from 'crypto';
const hash = crypto.createHash('md5').update(password).digest('hex');
// MD5 e SHA1 sono considerati insicuri per le password
セーフバージョン
import bcrypt from 'bcrypt';
// SICURO: hash con bcrypt e salt rounds
const SALT_ROUNDS = 12;
app.post('/api/register', async (req, res) => {
const hashedPassword = await bcrypt.hash(req.body.password, SALT_ROUNDS);
const user = new User({
email: req.body.email,
password: hashedPassword
});
await user.save();
});
// SICURO: verifica password
app.post('/api/login', async (req, res) => {
const user = await User.findOne({ email: req.body.email });
if (!user) return res.status(401).json({ error: 'Credenziali non valide' });
const isValid = await bcrypt.compare(req.body.password, user.password);
if (!isValid) return res.status(401).json({ error: 'Credenziali non valide' });
// Non rivelare se e' l'email o la password ad essere sbagliata
});
A03:2021 - 注射
Le 注射 無効化されたユーザーデータが来た場合に発生 クエリ、コマンド、またはインタープリタに直接挿入されます。 SQLインジェクション、NoSQLを含む インジェクション、コマンドインジェクション、XSS。
SQLインジェクション
// VULNERABILE: concatenazione diretta nella query
app.get('/api/products', async (req, res) => {
const query = `SELECT * FROM products WHERE name = '${req.query.name}'`;
// Un attaccante può inviare: ' OR '1'='1' --
const results = await db.query(query);
res.json(results);
});
// SICURO: query parametrizzate
app.get('/api/products', async (req, res) => {
const query = 'SELECT * FROM products WHERE name = $1';
const results = await db.query(query, [req.query.name]);
res.json(results);
});
NoSQLインジェクション
// VULNERABILE: input non sanitizzato in MongoDB
app.post('/api/login', async (req, res) => {
// Un attaccante può inviare: { "email": { "$gt": "" }, "password": { "$gt": "" } }
const user = await User.findOne({
email: req.body.email,
password: req.body.password
});
});
// SICURO: validazione e sanitizzazione dell'input
import { body, validationResult } from 'express-validator';
app.post('/api/login',
body('email').isEmail().normalizeEmail(),
body('password').isString().isLength({ min: 8 }),
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Prosegui con query sicura...
}
);
Angular と XSS
Angular は、クロスサイト スクリプティング (XSS) から自動的に保護します。
統合された消毒剤。二重中括弧によるバインド
HTML を自動的にエスケープします。ただし、
innerHTML o bypassSecurityTrustHtml これを無効にする
保護されており、細心の注意を払って使用する必要があります。
A04:2021 - 安全でない設計
L'安全でない設計 2021年に導入されたカテゴリーは、 実装ではなく、アプリケーション設計の欠陥について。それはについてです セキュリティモデル、脅威モデリング、および設計によるセキュリティ原則の欠如。
例: レート制限なし
// VULNERABILE: nessun rate limiting
app.post('/api/login', async (req, res) => {
// Un attaccante può provare milioni di password senza limiti
const user = await authenticate(req.body.email, req.body.password);
if (user) return res.json({ token: generateToken(user) });
return res.status(401).json({ error: 'Login fallito' });
});
// SICURO: rate limiting con express-rate-limit
import rateLimit from 'express-rate-limit';
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minuti
max: 5, // massimo 5 tentativi
message: { error: 'Troppi tentativi. Riprova tra 15 minuti.' },
standardHeaders: true,
legacyHeaders: false,
});
app.post('/api/login', loginLimiter, async (req, res) => {
// Logica di autenticazione...
});
安全な設計原則
- を導く 脅威モデリング コードを書く前に
- の原理を適用します 最低限の特権
- 埋め込む 多層防御: 複数レベルのセキュリティ
- 入力を検証します あらゆるレベル (クライアント、API、データベース)
- を考慮してください。 虐待のシナリオ 通常の使用例に加えて
A05:2021 - セキュリティの構成ミス
Le セキュリティの構成ミス 安全でないデフォルト設定が含まれる HTTP ヘッダーが欠落している、CORS が寛容すぎる、本番環境でスタック トレースが公開されている 不要な機能が有効になっています。
ヘルメット付きセキュリティヘッダー
import helmet from 'helmet';
import cors from 'cors';
const app = express();
// SICURO: header di sicurezza con Helmet
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
imgSrc: ["'self'", "data:", "https:"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
connectSrc: ["'self'", "https://api.example.com"],
}
},
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}));
// SICURO: CORS restrittivo
app.use(cors({
origin: ['https://miositoweb.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
// SICURO: nascondi dettagli errore in produzione
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(err.stack);
res.status(500).json({
error: process.env.NODE_ENV === 'production'
? 'Errore interno del server'
: err.message // Mostra dettagli solo in development
});
});
A06:2021 - 脆弱で古いコンポーネント
の使用 脆弱なコンポーネントまたは時代遅れのコンポーネント そして風土病の問題 最新のアプリケーション。単一の古い依存関係によって、依存関係全体が暴露される可能性があります。 既知の脆弱性への適用。
依存関係の制御
# Controlla le vulnerabilità note nelle dipendenze
npm audit
# Correggi automaticamente le vulnerabilità
npm audit fix
# Report dettagliato
npm audit --json
# Aggiorna le dipendenze in modo sicuro
npx npm-check-updates -u --target minor
# Usa Snyk per un'analisi più approfondita
npx snyk test
npx snyk monitor
ベストプラクティス
- 走る
npm audit定期的に (理想的には CI/CD で) - dependabot または Renovate を使用して依存関係を最新の状態に保つ
- 未使用の依存関係を削除する
- アメリカ合衆国
package-lock.json再現可能なビルド用 - 依存関係ライセンスのコンプライアンスを確認する
A07:2021 - 識別と認証の失敗
I 認証の失敗 含まれるもの: 弱いパスワードが受け入れられる、 有効期限のないセッション、誤って設定された JWT トークン、多要素認証の欠如。
JWT 確かに
import jwt from 'jsonwebtoken';
// VULNERABILE: segreto debole e nessuna scadenza
const token = jwt.sign({ userId: user.id }, 'secret123');
// SICURO: segreto forte, scadenza breve, algoritmo specificato
const JWT_SECRET = process.env.JWT_SECRET; // Almeno 256 bit, da variabile d'ambiente
const JWT_EXPIRY = '15m'; // Scadenza breve
const REFRESH_EXPIRY = '7d';
function generateTokens(user: User): TokenPair {
const accessToken = jwt.sign(
{ userId: user.id, role: user.role },
JWT_SECRET,
{
algorithm: 'HS256',
expiresIn: JWT_EXPIRY,
issuer: 'miositoweb.com',
audience: 'miositoweb.com'
}
);
const refreshToken = jwt.sign(
{ userId: user.id, tokenVersion: user.tokenVersion },
JWT_SECRET,
{ expiresIn: REFRESH_EXPIRY }
);
return { accessToken, refreshToken };
}
// Middleware di verifica
function authenticate(req: Request, res: Response, next: NextFunction): void {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
res.status(401).json({ error: 'Token mancante' });
return;
}
try {
const decoded = jwt.verify(token, JWT_SECRET, {
algorithms: ['HS256'],
issuer: 'miositoweb.com'
});
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ error: 'Token non valido o scaduto' });
}
}
A08:2021 - ソフトウェアとデータの整合性障害
I 整合性の欠如 それらは、そうではないコードとインフラストラクチャに関するものです。 不正な変更から保護します。含まれるもの: 安全でない CI/CD パイプライン、 検証なしの自動更新、信頼できないデータの逆シリアル化。
サブリソースの整合性 (SRI)
<!-- VULNERABILE: script esterno senza verifica -->
<script src="https://cdn.example.com/lib.js"></script>
<!-- SICURO: SRI verifica l'integrita del file -->
<script
src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8w"
crossorigin="anonymous">
</script>
CI/CD 保護
- アメリカ合衆国 署名付きコミット 誰が変更を加えたかを確認するため
- 設定する ブランチ保護ルール GitHub 上で
- パッケージの整合性を検証する
npm ciの代わりにnpm install - アメリカ合衆国 ロックファイル 再現可能なビルドを保証するため
- 埋め込む 必須のコードレビュー 合併前
A09:2021 - セキュリティのログ記録と監視の失敗
I ログの失敗 検出、拡大縮小、および 積極的な違反に対応します。セキュリティイベントをログに記録しないとわかりません 決して妥協したことがないこと。
構造化されたロギング
import winston from 'winston';
// Logger strutturato per eventi di sicurezza
const securityLogger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
defaultMeta: { service: 'auth-service' },
transports: [
new winston.transports.File({ filename: 'security.log' }),
new winston.transports.File({ filename: 'error.log', level: 'error' })
]
});
// Logga eventi di sicurezza critici
function logSecurityEvent(event: SecurityEvent): void {
securityLogger.info({
event: event.type,
userId: event.userId,
ip: event.ip,
userAgent: event.userAgent,
timestamp: new Date().toISOString(),
details: event.details
});
}
// Esempi di eventi da loggare:
// - Login riusciti e falliti
// - Tentativi di accesso non autorizzato
// - Modifiche ai permessi
// - Errori di validazione input sospetti
// - Creazione/eliminazione account
ログに記録する内容
- すべての ログイン試行 (成功も失敗も)
- すべて順調です アクセスが拒否されました (403禁止)
- Le 機密データの変更 (パスワード、メールアドレス、役割)
- Gli 検証エラー 攻撃を示している可能性があります
- Le 行政措置 (ユーザー作成、設定変更)
A10:2021 - サーバー側リクエスト フォージェリ (SSRF)
Il SSRF アプリケーションが HTTP リクエストを行うときに発生します 検証なしでユーザーが指定した URL に送信されます。攻撃者がサーバーを使用できる 内部サービス、クラウド メタデータ、またはプライベート ネットワークにアクセスするためのプロキシとして。
SSRFの例
import axios from 'axios';
import { URL } from 'url';
// VULNERABILE: l'utente controlla l'URL della richiesta
app.post('/api/fetch-url', async (req, res) => {
// Un attaccante può richiedere: http://169.254.169.254/latest/meta-data/
// per accedere ai metadata AWS
const response = await axios.get(req.body.url);
res.json(response.data);
});
// SICURO: validazione rigorosa dell'URL
const ALLOWED_DOMAINS = ['api.github.com', 'api.example.com'];
app.post('/api/fetch-url', async (req, res) => {
try {
const parsedUrl = new URL(req.body.url);
// Verifica il protocollo
if (!['https:'].includes(parsedUrl.protocol)) {
return res.status(400).json({ error: 'Solo HTTPS permesso' });
}
// Verifica il dominio contro una whitelist
if (!ALLOWED_DOMAINS.includes(parsedUrl.hostname)) {
return res.status(400).json({ error: 'Dominio non permesso' });
}
// Blocca IP privati e loopback
const blockedPatterns = [
/^127\./, /^10\./, /^172\.(1[6-9]|2\d|3[01])\./, /^192\.168\./,
/^169\.254\./, /^0\./, /localhost/i
];
if (blockedPatterns.some(p => p.test(parsedUrl.hostname))) {
return res.status(400).json({ error: 'Indirizzo non permesso' });
}
const response = await axios.get(parsedUrl.toString(), {
timeout: 5000,
maxRedirects: 0 // Blocca i redirect che potrebbero aggirare le protezioni
});
res.json(response.data);
} catch (err) {
res.status(400).json({ error: 'URL non valido' });
}
});
Angular 開発者向けのセキュリティ チェックリスト
以下は、最も重要な領域をカバーする Angular アプリケーション固有のチェックリストです。
クライアント側の保護
- 決して無効にしないでください 角サニタイザー 正当な理由なく
- 避ける
bypassSecurityTrustHtml内容が完全に信頼できるものでない限り - アメリカ合衆国 HTTP インターセプター トークンを追加して 401 エラーを処理する
- 埋め込む ルートガード 保護されたルートの場合は (CanActivate、CanLoad)
- 機密データを保存しないでください
localStorage(HttpOnly Cookie を優先します)
サーバー側の保護
- 有効 各入力 サーバー側 (クライアントを信頼しない)
- アメリカ合衆国 パラメータ化されたクエリ データベースとのやり取りごとに
- 埋め込む レート制限 すべての機密性の高いエンドポイントで
- 設定する コルス 限定的に
- すべて有効にする セキュリティヘッダー ヘルメット付き
- アメリカ合衆国 HTTPS どこでも例外なく
依存関係の管理
- 走る
npm auditすべての CI/CD ビルドで - 依存関係を定期的に更新する
- 再生可能なビルドにはロック ファイルを使用する
- 未使用の依存関係を削除する
認証とセッション
- アメリカ合衆国 bcrypt (または argon2) パスワードハッシュ用
- 埋め込む 有効期限が短い JWT + リフレッシュトークン
- を考慮してください。 多要素認証 重要なアクションの場合
- ログアウト時にセッションを無効にする
結論
Web アプリケーションのセキュリティは 1 回限りのチェックポイントではありません。 そして 連続プロセス の各フェーズに統合する必要があります。 開発。 OWASP Top 10 は、問題を特定し軽減するための優れたフレームワークを提供します。 最も一般的な脆弱性ですが、これは出発点にすぎません。
開発者として、私たちにはデフォルトで安全なコードを記述する責任があります。 この記事で紹介するベスト プラクティスはオプションではありません。 最低限の 不可欠な 本番環境のあらゆる Web アプリケーションに対応します。実装から始める アクセス制御 (A01) とデータ保護 (A02) を経て、段階的に拡張します。 あなたの安全姿勢を。
覚えておいてください: セキュリティは決して「終わった」わけではありません。新しい脆弱性を監視し、更新する 依存関係を監視し、定期的な監査を実行し、そして何よりも学習を決してやめません。費用 脆弱性を防ぎ、その結果に苦しむコストよりも常に低く抑えることができます。







