概要: このチュートリアルでは、express-validatorライブラリを使用して入力データを検証およびサニタイズする方法を学びます。
Expressアプリケーションがユーザー入力などの外部ソースからデータを受信する場合、入力データを検証およびサニタイズすることが重要です。
- データ検証は、入力データが型、範囲、形式などの特定の基準を満たしていることを保証します。入力データが有効であることを確認します。たとえば、メールアドレスが有効な形式であるかどうかを確認できます。
- データサニタイズは、有害な文字を削除またはエスケープすることにより、入力データをクリーンアップします。コードインジェクションなどの悪意のある入力を防ぐのに役立ちます。たとえば、Webページにレンダリングする前に入力データからHTMLタグまたはスクリプトタグを削除できます。
Expressアプリケーションでは、入力データを手動で検証およびサニタイズできますが、このプロセスには時間がかかり、より多くの労力が必要です。
より効率的にするために、サードパーティの検証ライブラリを利用できます。このチュートリアルでは、express-validatorライブラリを使用して入力データを検証およびサニタイズする方法を学びます。
クエリ文字列の検証
まず、簡単なExpressアプリケーションを作成します
import express from 'express';
const PORT = process.env.PORT || 3000;
const app = express();
app.get('/hi', (req, res) => res.send(`Hi, ${req.params.name}!`));
app.listen(PORT, () => console.log(`The server is listening on port ${PORT}`));Code language: JavaScript (javascript)このアプリには、クエリ文字列nameを受け入れるルート/hiが含まれています。Webブラウザに挨拶メッセージが表示されます。
次に、ターミナルで次のnpmコマンドを実行して、プロジェクトにexpress-validatorライブラリをインストールします
npm install express-validatorCode language: JavaScript (javascript)3番目に、次のエンドポイントへのリクエストを作成します
http://:3000/hi?name=JohnCode language: JavaScript (javascript)Webブラウザに次のメッセージが表示されます
Hi, John!Code language: JavaScript (javascript)ただし、nameクエリ文字列を渡さずに次のエンドポイントをリクエストすると
http://:3000/hiCode language: JavaScript (javascript)画面に次のメッセージが表示されます
Hi, undefinedCode language: JavaScript (javascript)その理由は、この例ではreq.query.nameがundefinedであるためです。
最悪の場合、クエリ文字列にJavaScriptコードが含まれていると、悪意のあるページにリダイレクトされる可能性があります。例:
http://:3000/hi?name=<script>window.location ='https://www.google.com';</script>Code language: JavaScript (javascript)この例では、google.comにリダイレクトするコードを注入します。実際のシナリオでは、悪意のあるページにリダイレクトされる可能性があります。これはクロスサイトスクリプティング(XSS)と呼ばれます。
クロスサイトスクリプティング(XSS)は、攻撃者が悪意のある実行可能スクリプトを信頼できるWebサイトのコードに注入する攻撃です。
nameクエリ文字列の値を検証およびサニタイズするには、express-validatorライブラリを使用できます。
技術的には、express-validatorライブラリは、入力データを検証およびサニタイズするミドルウェア関数のセットです。
4番目に、express-validatorライブラリの関数を使用してnameクエリ文字列を検証します
import express from 'express';
import { query, validationResult, matchedData } from 'express-validator';
const PORT = process.env.PORT || 3000;
const app = express();
app.get('/hi', query('name').notEmpty().escape(), (req, res) => {
// validate data
const result = validationResult(req);
if (!result.isEmpty()) {
res.status(400).send({ errors: result.array() });
}
// sanitize data
const cleanedData = matchedData(req);
res.send(`Hi, ${cleanedData.name}!`);
});
app.listen(PORT, () => console.log(`The server is listening on port ${PORT}`));Code language: JavaScript (javascript)仕組み。
ステップ1。検証を処理するためにexpress-validatorライブラリから関数をインポートします
import { query, validationResult, matchedData } from 'express-validator';Code language: JavaScript (javascript)インポートされた関数の概要は次のとおりです
query()関数は、クエリパラメータを検証するミドルウェアです。validationResult()関数は、受信リクエストの検証とサニタイズの結果を収集します。matchedData()関数は、検証およびサニタイズされたデータを抽出します。
ステップ2. nameクエリ文字列を検証します
query('name').notEmpty().escape()Code language: JavaScript (javascript)この構文では
notEmpty()関数は、nameクエリ文字列の値が空でないことを保証します。escape()関数は、XSS攻撃を防ぐためにフィールド値をエスケープします。
query()、notEmpty()、およびescape()関数は、検証チェーンを形成します。ミドルウェア関数を返すため、app.get()メソッドで使用できます。
ステップ3. validationResult()関数を使用して検証結果を取得します
const result = validationResult(req);
if (!result.isEmpty()) {
res.status(400).send({ errors: result.array() });
}Code language: JavaScript (javascript)query()関数は、検証エラーを自動的に報告しません。代わりに、検証エラーを収集し、validationResult()関数を使用して検証します。validationResult()関数は、ValidationErrorオブジェクトを返します。
ValidationErrorオブジェクトのisEmpty()メソッドは、検証エラーが発生しなかった場合はtrueを返し、それ以外の場合はfalseを返します。コードでは、HTTPステータスコード400で検証エラーを含むJSON応答を返します。
ステップ4. サニタイズされたデータを取得します
サニタイズされたデータを取得するには、matchedData()関数を使用します
const cleanedData = matchedData(req);Code language: JavaScript (javascript)ステップ5. サニタイズされたデータをレンダリングします
res.send(`Hi, ${cleanedData.name}!`);Code language: JavaScript (javascript)ルートパラメータの検証
ルートパラメータを検証するには、express-validatorライブラリのparam()関数を使用できます。
param()関数を使用すると、ルーターパラメータの検証ルールを指定できます。
次の例は、param()関数を使用してidルートパラメータを検証する方法を示しています
app.get(
'/api/todos/:id',
param('id').isInt({ min: 1 }).withMessage('ID must be a positive integer'),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const id = req.params.id;
res.send(`Fetching record with ID: ${id}`);
}
);Code language: JavaScript (javascript)仕組み。
param('id')は、検証のためのidルートパラメータを指定します
isInt({ min: 1 })は、idパラメータが整数で、少なくとも1であることを保証します。withMessage('IDは正の整数でなければなりません')は、検証に失敗した場合にカスタムエラーメッセージを指定します。
残りのコードは、クエリ文字列の検証と同じように機能します。
リクエストボディの検証
ステップ1. リクエストをJSONオブジェクトとして処理するには、express.json()と呼ばれる組み込みミドルウェアを登録します
app.use(express.json());Code language: JavaScript (javascript)これにより、Expressアプリケーションに、ボディをJSONオブジェクトとして解析するように指示し、req.bodyプロパティを使用してアクセスできるようになります。
JSONオブジェクトのフィールドにアクセスするには、express-validatorライブラリのbody()関数を使用できます。
ステップ2. express-validatorライブラリからbody関数をインポートします
import {body, validationResult, matchedData} from 'express-validator';Code language: JavaScript (javascript)ステップ3. リクエストのボディにあるtitleフィールドとcompletedフィールドを検証します
app.post('/api/todos',
[body('title').notEmpty().isString(), body('completed').isBoolean()],
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).send({ errors: errors.array() });
}
const record = matchedData(req);
res.send(record);
}
);Code language: JavaScript (javascript)この例では、app.post()関数にミドルウェアの配列を渡します。最初のミドルウェアはtitleフィールドを検証し、2番目のミドルウェアはリクエストボディのcompletedフィールドを検証します。
たとえば、リクエストボディにtitleフィールドとcompletedフィールドを指定せずに/api/todosエンドポイントにPOSTリクエストを行うと、次のエラーが発生します
{
"errors": [
{
"type": "field",
"msg": "Invalid value",
"path": "title",
"location": "body"
},
{
"type": "field",
"msg": "Invalid value",
"path": "title",
"location": "body"
},
{
"type": "field",
"msg": "Invalid value",
"path": "completed",
"location": "body"
}
]
}Code language: JavaScript (javascript)概要
- クエリ文字列を検証するには、
query()関数を使用します。 - ルートパラメータを検証するには、
param()関数を使用します。 - リクエストボディを検証するには、
body()関数を使用します。