Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/labs/deserialization.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,35 @@ info =
{
absent: "^ const data =\n",
text: "The first section should begin with `const data =`",
text_ja: "最初のセクションは、`const data =`で始めなければなりません。",
},
{
present: "json",
text: "the JSON built-in global object is witten in uppercase.",
text_ja: "JSONというビルトインのグローバルオブジェクトは全て大文字で書かれます",
},
{
absent: String.raw`JSON \. parse
`,
text: "Make a call to `JSON.parse` with the data retrieved, e.g., `JSON.parse(base64Decoded)` should be stored in `data`.",
text_ja: "取得したデータに対して`JSON.parse`を呼んでください。例: `JSON.parse(base64Decoded)`は`data`に格納されなければなりません",
},
{
present: String.raw`\+
`,
text: "You should not have any concatenation (`+`) in the first section.",
text_ja: "最初のセクションでは、文字列連結(`+`)を用いるべきではありません。",
},
{
absent: "; $\n",
text: "JavaScript does not require semicolons at the end of a statement, but since the other statements terminate with semicolons, you should also terminate your statement with a semicolon to be consistent.",
text_ja: "JavaScriptではステートメントの最後にセミコロンは必須ではありませんが、他のステートメントはセミコロンで終わっているため、一貫性を保つためにセミコロンでステートメントを終わらせなければなりません。",
},
{
absent: String.raw`^ if \(`,
index: 1,
text: "The second section should start with `if (` followed by a condition.",
text_ja: "二番目のセクションは、`if (`で始めてその次に条件を書かなくてはなりません。",
examples: [
[
"const data = JSON.parse(base64Decoded);",
Expand All @@ -72,33 +78,39 @@ info =
`,
index: 1,
text: "Check if the data object has a property called username. You can do this by referencing data.username.",
text_ja: "データオブジェクトがusernameというプロパティを持っているかをチェックしてください。これは、data.usernameを参照することによって行うことができます。",
},
{
absent: String.raw`\&\&`,
index: 1,
text: "To combine multiple conditions in JavaScript use &&. This operator means 'and', so both conditions must be true for the entire statement to pass.",
text_ja: "JavaScriptで複数の条件を結合するには&&を用います。この演算子は「かつ」(AND)を意味し、ステートメント全体をパスするためには両方の条件が真でなければなりません。",
},
{
absent: "typeof",
index: 1,
text: "Use typeof to check the type of the operand's value. You should have `typeof data.username == 'string'` or similar.",
text_ja: "typeofを用いてオペランドの値の型をチェックしてください。`typeof data.username == 'string'`のようになるはずです。",
},
{
present: String.raw`typeof data \. username == 'String'
`,
index: 1,
text: "When using typeof, JavaScript expects \"string\" all lowercase.",
text_ja: "JavaScriptでtypeofを使う際には、\"string\"を全て小文字で表記します。",
},
{
absent: "length",
index: 1,
text: "check if the length of the string is smaller than 20 characters. Use the expression `data.username.length < 20` to determine this.",
text_ja: "文字列の長さが20文字より小さいことをチェックしてください。`data.username.length < 20`というエクスプレッションを使って判断してください。",
},
{
present: String.raw`^ if \(`,
absent: String.raw`^ if \( data \. username &&`,
index: 1,
text: "Begin the second section with `if ( data.username && ... ` because you must check if data is even present before you can check various attributes of that data.",
text_ja: "二番目のセクションを`if ( data.username && ... `で始めてください。これは、データのいろいろなアトリビュートをチェックする前に、それがそもそも存在するかをチェックしなければならないからです。",
examples: [
[
"const data = JSON.parse(base64Decoded);",
Expand Down
114 changes: 114 additions & 0 deletions docs/labs/ja_deserialization.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://best.openssf.org/assets/css/style.css">
<link rel="stylesheet" href="checker.css">
<script src="checker.js"></script>
<script src="deserialization.js"></script>
<link rel="license" href="https://creativecommons.org/licenses/by/4.0/">

<!-- See create_labs.md for how to create your own lab! -->

</head>
<body>
<!-- For GitHub Pages formatting: -->
<div class="container-lg px-3 my-5 markdown-body">
<h1>ラボ演習 deserialization</h1>
<p>
これはセキュアなソフトウェア開発に関するラボ演習です。
ラボの詳細については、<a href="ja_introduction.html" target="_blank">概要</a>をご覧ください。

<p>
<h2>タスク</h2>
<p>
<b>下記のコードを変更して、安全でないデシリアライゼーション(Insecure Deserialization)の脆弱性を防止するようにしてください。</b>

<p>
<h2>背景</h2>
<p>
安全でないデシリアライゼーションは、アプリケーションのデシリアライゼーションのプロセスが悪用されて起こり、攻撃者がシリアライズされたデータを改変してアプリケーションコードに悪影響を及ぶデータを渡すことができてしまうものです。
<p>
この脆弱性を防ぐ最も安全な方法は、信頼できないソース(ユーザーがコントロールできるデータ)からシリアライズされたオブジェクトを受け入れないようにすることです。しかし、もし受け入れる必要があるときには、実装することのできる緩和策がいくつかあります。このラボでは、そのような緩和策をいくつか適用します。

<p>
<h2>タスクの詳細</h2>
<p>

<p>
下記のコードは、アプリケーションのログインページの後に呼ばれます。ログイン後、ユーザーのプロファイルを含むクッキーが作られ、ホームページでクッキーはデシリアライズされ、その中のユーザー名(username)を挨拶のメッセージで使います。
<p>
このコードをよりよく見ると、クッキーからデータをデシリアライズする際に<tt>eval()</tt>を用いていることがわかります。これは非常に危険となりうるもので、<tt>eval()</tt>は文字列をJavaScriptのコードとして評価するために、文字列に含まれる任意のコードが実行されることとなり、リモートコード攻撃(RCE)やコードインジェクション攻撃の可能性を開くものです。
<p>
このラボでは、攻撃者がコードを実行することを防ぐアプローチを用いてこれを修正します。また、簡単な入力のバリデーションを行うことで、JSONデータから得られるものが期待通りものであることを確認します。
<ol>
<li>
<tt>eval()</tt>を<tt>JSON.parse()</tt>で置き換えてください。JSON.parse( )は関数やメソッドといったJavaScriptのコードを実行することはないため、デシリアライゼーションへのはるかに安全なアプローチです。
</li>
<li><tt>data.username</tt>が存在するかどうかのチェックに加えて、その値に対して簡単なバリデーションを行ってください。値が文字列であり(<tt>typeof data.username == 'string'</tt>)、それが20文字未満である(<tt>data.username.length &lt; 20</tt>)ことを確認してください</li>
</ol>

<p>
必要に応じて、「ヒント」ボタンと「諦める」ボタンを使用してください。

<p>
<h2>演習 (<span id="grade"></span>)</h2>
<p>
<p>

下記のコードを変更して、安全でないデシリアライゼーションを防ぐ緩和策を追加してください。
<ol>
<li>信頼できないコードの実行を防ぐデシリアライゼーションのアプローチを用いてください。</li>
<li>ユーザー名(username)をバリデートし、それが<b>文字列であり</b><b>20文字</b>未満である場合にのみ応答するようにしてください。</li>
</ol>
<form id="lab">

<pre><code
>const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();

app.use(express.json());
app.use(cookieParser());

app.get('/', (req, res) => {
if (req.cookies.profile) {
try {
const base64Decoded = Buffer.from(
req.cookies.profile, 'base64').toString('utf8');
<input id="attempt0" type="text" size="60" spellcheck="false"
value="const data = eval('(' + base64Decoded + ')');">

<textarea id="attempt1" rows="3" cols="60" spellcheck="false"
>if (data.username) {</textarea>
// XSSを防ぐために、信頼できないデータでres.sendを用いることは避けてください。
res.render('index', {username: data.username});
}

} catch (err) {
res.send('An error occurred.');
}
} else {
res.send("Please Login");
}
});
</code></pre>


<button type="button" class="hintButton">ヒント</button>
<button type="button" class="resetButton">リセット</button>
<button type="button" class="giveUpButton">諦める</button>
<br><br>
<p>
<i>このラボは、Camila Vilarinhoによって開発されました。</i>
<br><br>
<p id="correctStamp" class="small">
<textarea id="debugData" class="displayNone" rows="20" cols="65" readonly>
</textarea>
</form>
</div><!-- End GitHub pages formatting -->
</body>
</html>