



WithCodeMedia-1-pc
WithCodeMedia-2-pc
WithCodeMedia-3-pc
WithCodeMedia-4-pc




WithCodeMedia-1-sp
WithCodeMedia-2-sp
WithCodeMedia-3-sp
WithCodeMedia-4-sp









生徒Web制作でセキュリティって本当に重要なんですか?個人サイトでも対策が必要でしょうか?



非常に重要じゃ!セキュリティの甘いサイトは、個人情報漏洩や改ざん、マルウェア配布の踏み台にされる危険があるんじゃよ。今日は実務で必須のセキュアコーディングチェックリストを詳しく解説するぞい!
現代のWeb制作において、セキュリティ対策は避けて通れない重要課題です。小規模サイトから大企業まで、あらゆるWebサイトが攻撃の標的となっており、サイバー攻撃件数は年々増加しています。
本記事では、Web制作における必須のセキュアコーディングチェックリストを、実装例と共に徹底的に解説します。初心者の方でも理解できるよう、各項目の重要性と具体的な対策方法を丁寧に説明します。
セキュアコーディング(Secure Coding)とは、セキュリティ上の脆弱性を作り込まないようにコードを書く開発手法のことです。単にバグを減らすだけでなく、悪意のある攻撃者に悪用される可能性のある脆弱性を事前に防ぐことを目的としています。
Web制作においてセキュアコーディングが重要な理由は以下の通りです。
OWASP(The Open Web Application Security Project)が公開している「OWASP Top 10」は、Webアプリケーションにおける最も深刻なセキュリティリスクをまとめたものです。2021年版の主要な脆弱性は以下の通りです。
| 順位 | 脆弱性の種類 | 説明 |
|---|---|---|
| 1 | アクセス制御の不備 | 認証・認可の実装ミスによる不正アクセス |
| 2 | 暗号化の失敗 | データ転送・保存時の暗号化不足 |
| 3 | インジェクション | SQLi、XSSなどの不正な入力 |
| 4 | 安全でない設計 | セキュリティを考慮しない設計 |
| 5 | セキュリティ設定ミス | デフォルト設定のまま使用など |
| 6 | 脆弱なコンポーネント | 古いライブラリやフレームワークの使用 |
| 7 | 識別と認証の失敗 | 脆弱なパスワードポリシーなど |
| 8 | ソフトウェアとデータの整合性 | 検証なしのアップデートなど |
| 9 | ログと監視の不足 | 攻撃の検知・対応ができない |
| 10 | サーバーサイドリクエストフォージェリ | SSRF攻撃への脆弱性 |
本記事では、これらの脆弱性に対する具体的な対策をチェックリスト形式で紹介します。



まずは何から始めれば良いですか?



まずは基本中の基本から押さえるんじゃ!これらは全てのWebサイトで必須の対策じゃぞ
最も基本的かつ重要なセキュリティ対策です。Googleは2014年以降HTTPSをランキングシグナルとして使用しており、HTTPサイトには警告が表示されます。また、HTTP/2の高速化機能もHTTPS必須です。
# .htaccess
# HTTPからHTTPSへのリダイレクト
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# HSTSヘッダーの設定(1年間)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<!-- Content Security Policyで混在コンテンツをブロック -->
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
<!-- 相対URLまたはプロトコル相対URLを使用 -->
<link rel="stylesheet" href="/css/style.css">
<script src="//example.com/script.js"></script>
</head>HTTPレスポンスヘッダーにセキュリティ関連の設定を追加することで、多くの攻撃を防げます。セキュリティヘッダーは「深層防御(Defense in Depth)」の一環として、複数の層でセキュリティを強化する重要な手段です。
# .htaccess
<IfModule mod_headers.c>
# クリックジャッキング対策
Header always set X-Frame-Options "SAMEORIGIN"
# MIMEタイプスニッフィング防止
Header always set X-Content-Type-Options "nosniff"
# XSS対策(古いブラウザ用)
Header always set X-XSS-Protection "1; mode=block"
# Content Security Policy(厳格な設定例)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'self';"
# リファラーポリシー
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# 機能ポリシー
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
</IfModule>// helmet.jsを使った簡単な設定
const helmet = require('helmet')
const express = require('express')
const app = express()
// デフォルトのセキュリティヘッダーを設定
app.use(helmet())
// カスタマイズした設定
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "https://trusted-cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
})
)インジェクション攻撃は、OWASP Top 10で常に上位にランクインする深刻な脆弱性です。ユーザーからの入力を直接クエリや命令に使うコードは必ず見直してください。
データベースへの不正な命令を実行される攻撃を防ぎます。プリペアドステートメント(パラメータ化クエリ)の使用が最も確実な対策です。
<?php
// 危険!ユーザー入力を直接SQLに埋め込んでいる
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($conn, $query);
// 攻撃例: username に "admin' --" を入力すると
// SELECT * FROM users WHERE username = 'admin' --' AND password = ''
// パスワードチェックが無効化される
?><?php
// 安全:プリペアドステートメントを使用
$username = $_POST['username'];
$password = $_POST['password'];
// パスワードはハッシュ化して保存されている前提
$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password_hash = ?");
$stmt->bind_param("ss", $username, password_hash($password, PASSWORD_BCRYPT));
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 1) {
$user = $result->fetch_assoc();
// パスワード検証
if (password_verify($password, $user['password_hash'])) {
// ログイン成功
echo "ログイン成功";
} else {
echo "認証失敗";
}
} else {
echo "認証失敗";
}
$stmt->close();
?>// Prisma ORMを使った安全な実装
import { PrismaClient } from '@prisma/client'
import bcrypt from 'bcrypt'
const prisma = new PrismaClient()
async function login(username: string, password: string) {
// Prismaは自動的にパラメータ化される
const user = await prisma.user.findUnique({
where: { username: username }
})
if (!user) {
throw new Error('認証失敗')
}
// パスワード検証
const isValid = await bcrypt.compare(password, user.passwordHash)
if (!isValid) {
throw new Error('認証失敗')
}
return user
}悪意のあるスクリプトがWebページに埋め込まれる攻撃を防ぎます。ユーザー入力を出力する全ての箇所でエスケープ処理が必須です。
// 危険!ユーザー入力をそのままHTMLに挿入
const userName = new URLSearchParams(window.location.search).get('name')
document.getElementById('greeting').innerHTML = `こんにちは、${userName}さん`
// 攻撃例: ?name=<script>alert('XSS')</script>
// スクリプトが実行されてしまう// 安全:textContentを使用(自動的にエスケープされる)
const userName = new URLSearchParams(window.location.search).get('name')
document.getElementById('greeting').textContent = `こんにちは、${userName}さん`
// または、手動でエスケープ
function escapeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
}
const userName = new URLSearchParams(window.location.search).get('name')
document.getElementById('greeting').innerHTML = `こんにちは、${escapeHTML(userName)}さん`// React は自動的にエスケープする
function Greeting({ userName }: { userName: string }) {
// 安全:JSX構文は自動的にエスケープされる
return <div>こんにちは、{userName}さん</div>
}
// HTMLを挿入する必要がある場合はDOMPurifyでサニタイズ
import DOMPurify from 'dompurify'
function RichContent({ html }: { html: string }) {
const sanitizedHTML = DOMPurify.sanitize(html)
return (
<div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />
)
}<?php
// 安全:htmlspecialcharsでエスケープ
$userName = $_GET['name'] ?? 'ゲスト';
$escapedName = htmlspecialchars($userName, ENT_QUOTES, 'UTF-8');
echo "こんにちは、" . $escapedName . "さん";
// またはテンプレートエンジンを使用(Twigの例)
// {{ userName }} は自動的にエスケープされる
?>ユーザーの意図しないリクエストが実行される攻撃を防ぎます。フォーム送信・状態変更操作には必ずCSRFトークンを実装してください。
<?php
session_start();
// トークン生成(フォーム表示時)
function generateCSRFToken() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
// トークン検証(フォーム送信時)
function verifyCSRFToken($token) {
if (!isset($_SESSION['csrf_token']) || $token !== $_SESSION['csrf_token']) {
die('CSRF検証に失敗しました');
}
return true;
}
// フォーム表示
$csrfToken = generateCSRFToken();
?>
<form method="POST" action="update_profile.php">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
<input type="text" name="username">
<button type="submit">更新</button>
</form>
<?php
// フォーム処理
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verifyCSRFToken($_POST['csrf_token']);
// 処理を続行
$username = $_POST['username'];
// データベース更新など
}
?>const express = require('express')
const csrf = require('csurf')
const cookieParser = require('cookie-parser')
const app = express()
// Cookieパーサーとセッションミドルウェア
app.use(cookieParser())
app.use(express.urlencoded({ extended: false }))
// CSRF保護ミドルウェア
const csrfProtection = csrf({ cookie: true })
// フォーム表示
app.get('/profile', csrfProtection, (req, res) => {
res.render('profile', { csrfToken: req.csrfToken() })
})
// フォーム送信処理
app.post('/profile', csrfProtection, (req, res) => {
// CSRFトークンが自動的に検証される
const { username } = req.body
// 処理を続行
res.send('プロフィールを更新しました')
})// Server Action(Next.js 13+)
'use server'
import { cookies } from 'next/headers'
export async function updateProfile(formData: FormData) {
// Next.js Server Actionsは自動的にCSRF保護される
const username = formData.get('username') as string
// データベース更新など
return { success: true }
}
// クライアントコンポーネント
'use client'
import { updateProfile } from '@/app/actions'
export default function ProfileForm() {
return (
<form action={updateProfile}>
<input type="text" name="username" />
<button type="submit">更新</button>
</form>
)
}平文でパスワードを保存することは絶対に避けてください。bcryptやArgon2などのハッシュ関数を使い、必ずソルトを付与して保存します。
import bcrypt from 'bcrypt'
// ユーザー登録時
async function registerUser(username: string, password: string) {
// パスワード強度チェック
if (password.length < 8) {
throw new Error('パスワードは8文字以上である必要があります')
}
// パスワードをハッシュ化(saltRounds=10)
const saltRounds = 10
const passwordHash = await bcrypt.hash(password, saltRounds)
// データベースに保存
await db.user.create({
data: {
username,
passwordHash, // ハッシュ化されたパスワードを保存
}
})
// 平文パスワードはメモリから削除される
}
// ログイン時
async function login(username: string, password: string) {
const user = await db.user.findUnique({
where: { username }
})
if (!user) {
// タイミング攻撃対策のため、同じエラーメッセージ
throw new Error('ユーザー名またはパスワードが正しくありません')
}
// パスワード検証
const isValid = await bcrypt.compare(password, user.passwordHash)
if (!isValid) {
throw new Error('ユーザー名またはパスワードが正しくありません')
}
return user
}セッションIDの安全な生成・管理が、セッションハイジャック攻撃を防ぐ鍵です。ログイン前後でセッションを再生成し、タイムアウトを設定してください。
const express = require('express')
const session = require('express-session')
const app = express()
app.use(
session({
secret: process.env.SESSION_SECRET, // 環境変数から読み込む
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // JavaScriptからアクセス不可
secure: true, // HTTPS必須
sameSite: 'strict', // CSRF対策
maxAge: 1000 * 60 * 60, // 1時間
},
name: 'sessionId', // デフォルトの名前を変更
})
)
// ログイン処理
app.post('/login', async (req, res) => {
const { username, password } = req.body
const user = await authenticateUser(username, password)
if (user) {
// セッション再生成(セッション固定攻撃対策)
req.session.regenerate((err) => {
if (err) {
return res.status(500).send('セッションエラー')
}
req.session.userId = user.id
req.session.username = user.username
res.send('ログイン成功')
})
} else {
res.status(401).send('認証失敗')
}
})
// ログアウト処理
app.post('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.status(500).send('ログアウトエラー')
}
res.clearCookie('sessionId')
res.send('ログアウト成功')
})
})


WordPressサイトを運営しているんですが、特に気をつけることはありますか?



WordPressは世界中で使われているから、攻撃の標的になりやすいんじゃ。しっかり対策するんじゃぞ!
WordPressはCMS市場シェアが高いため、攻撃スクリプトが自動化されやすい環境です。本体・テーマ・プラグインのアップデートを怠ると、既知の脆弱性を突かれるリスクが高まります。
<?php
// wp-config.php
// セキュリティキーの設定(https://api.wordpress.org/secret-key/1.1/salt/ で生成)
define('AUTH_KEY', 'ランダムな文字列');
define('SECURE_AUTH_KEY', 'ランダムな文字列');
define('LOGGED_IN_KEY', 'ランダムな文字列');
define('NONCE_KEY', 'ランダムな文字列');
define('AUTH_SALT', 'ランダムな文字列');
define('SECURE_AUTH_SALT', 'ランダムな文字列');
define('LOGGED_IN_SALT', 'ランダムな文字列');
define('NONCE_SALT', 'ランダムな文字列');
// デバッグモードを無効化
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);
// ファイル編集を無効化
define('DISALLOW_FILE_EDIT', true);
define('DISALLOW_FILE_MODS', true);
// データベーステーブル接頭辞を変更
$table_prefix = 'wpsec_'; // デフォルトの「wp_」から変更
// SSL強制
define('FORCE_SSL_ADMIN', true);
// セキュリティヘッダー
@ini_set('display_errors', 0);
?># .htaccess
# wp-config.phpへのアクセスを拒否
<files wp-config.php>
order allow,deny
deny from all
</files>
# xmlrpc.phpへのアクセスを拒否(使用しない場合)
<files xmlrpc.php>
order allow,deny
deny from all
</files>
# wp-includes/へのアクセスを制限
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>
# ディレクトリリスティングを無効化
Options -Indexes
# 管理画面へのIPアドレス制限(オプション)
<Files wp-login.php>
order deny,allow
deny from all
# 許可するIPアドレスを指定
allow from 123.456.789.0
</Files>無料のWebアプリケーション脆弱性スキャナーです。Dockerで手軽に実行できます。
# Docker で OWASP ZAP を実行
docker run -t owasp/zap2docker-stable zap-baseline.py -t https://your-website.com
# レポートが生成されるWebサイトのセキュリティヘッダーを診断するツールです。
URL: https://observatory.mozilla.org/
SSL/TLS設定を詳細に診断し、評価グレードを表示します。
URL: https://www.ssllabs.com/ssltest/
# Node.jsプロジェクトの脆弱性チェック
npm audit
# 自動修正
npm audit fix
# または yarn
yarn audit


チェック項目がたくさんありますね!全部一度にやるのは大変そう…



その通りじゃ!まずは重要度の高い項目から順番に対策していくのが良いぞ。HTTPS対応、パスワードのハッシュ化、XSS/SQLi対策あたりから始めるんじゃ!



わかりました!このチェックリストを使って、安全なWebサイトを作ります!
はい、必要です。規模にかかわらず、HTTPS化・セキュリティヘッダー設定・CMSの最新化は必須です。小規模サイトは対策が手薄なケースが多く、自動化された攻撃スクリプトの標的になりやすい傾向があります。マルウェア配布の踏み台にされるとSEO評価にも深刻な影響を及ぼします。
どちらも深刻ですが、性質が異なります。SQLインジェクションはデータベースの情報漏洩・改ざん・削除に直結し、サービス全体に影響します。XSSはユーザーのブラウザ上でスクリプトを実行させ、セッション盗用や意図しない操作を引き起こします。OWASP Top 10では両方が上位に位置づけられており、どちらも同等に対策が必要です。
可能であれば両方を組み合わせるのがベストです。SameSite=Strict または Lax の Cookie 設定はモダンブラウザで有効ですが、古いブラウザや一部の状況では機能しないことがあります。CSRFトークンはより確実な対策であり、特に重要な操作(支払い・パスワード変更など)には必ず実装してください。
Jetpackなどxmlrpcを必要とするプラグインを使用していなければ、無効化することを強く推奨します。xmlrpc.phpはブルートフォース攻撃やDDoS攻撃に悪用されることが多く、使用しないなら.htaccessでアクセスをブロックするのが安全です。
基本的な監査はOWASP ZAP・Mozilla Observatory・SSL Labs SSL Testなどの無料ツールで十分対応できます。ただし、本番環境での本格的なペネトレーションテストや継続的な脆弱性監視が必要な場合は、有料の専門サービスやSnykなどの商用ツールを検討してください。
本記事では、Web制作におけるセキュアコーディングの完全チェックリストを、実装例と共に詳しく解説しました。
このセキュアコーディングチェックリストを活用し、安全で信頼性の高いWebサイトを構築してください。セキュリティは一度対策すれば終わりではなく、継続的に維持・改善していく必要があります。定期的にチェックリストを見直し、常に最新のセキュリティ対策を実施することで、ユーザーに安心して使ってもらえるWebサイトを提供できます。


副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?
未経験でも心配することはありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!
公式サイト より
今すぐ
無料カウンセリング
を予約!