chore(learn): Applied MDX format to Chinese curriculum files (#40462)
This commit is contained in:
@ -1,50 +1,55 @@
|
||||
---
|
||||
id: 587d8248367417b2b2512c3c
|
||||
title: 要求浏览器通过HTTPS访问您的站点仅限于使用helmet.hsts()
|
||||
challengeType: 2
|
||||
forumTopicId: 301573
|
||||
title: 要求浏览器通过HTTPS访问您的站点仅限于使用helmet.hsts()
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
HTTP 严格安全传输(HSTS)是一个能帮助我们抵御协议 <a href="https://en.wikipedia.org/wiki/Downgrade_attack">降级攻击</a> 和 <a href="https://en.wikipedia.org/wiki/Session_hijacking">Cookie 挟持</a> 的 WEB 安全协议。如果你的网站能够通过 HTTPS 访问,那么你就可以让你的用户避免使用不安全的 HTTP 协议访问。为此,你只需要在 header 中设置 Strict-Transport-Security 字段,以此来告诉浏览器在今后的一段时间内使用 HTTPS 来请求网站内容。这一设置对首次请求之后所以的后续请求都适用。
|
||||
</section>
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
HTTP 严格安全传输(HSTS)是一个能帮助我们抵御协议 [降级攻击](https://en.wikipedia.org/wiki/Downgrade_attack) 和 [Cookie 挟持](https://en.wikipedia.org/wiki/Session_hijacking) 的 WEB 安全协议。如果你的网站能够通过 HTTPS 访问,那么你就可以让你的用户避免使用不安全的 HTTP 协议访问。为此,你只需要在 header 中设置 Strict-Transport-Security 字段,以此来告诉浏览器在今后的一段时间内使用 HTTPS 来请求网站内容。这一设置对首次请求之后所以的后续请求都适用。
|
||||
|
||||
# --instructions--
|
||||
|
||||
通过 `helmet.hsts()`,网站会在未来的90天内使用 HTTPS。我们还可以传入配置对象 `{maxAge: timeInSeconds, force: true}`。Repl.it 默认已经开启 hsts,但你仍然可以通过添加 `{force: true}` 来覆盖它。我们会拦截 Glitch 请求的 header 来进行此挑战的测试,然后恢复此项配置。
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
通过 <code>helmet.hsts()</code>,网站会在未来的90天内使用 HTTPS。我们还可以传入配置对象 <code>{maxAge: timeInSeconds, force: true}</code>。Repl.it 默认已经开启 hsts,但你仍然可以通过添加 <code>{force: true}</code> 来覆盖它。我们会拦截 Glitch 请求的 header 来进行此挑战的测试,然后恢复此项配置。
|
||||
注意: 配置 HTTPS 需要域名以及 SSL/TSL 证书。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
# --hints--
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确加载 helmet.hsts() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'hsts'); assert.property(data.headers, 'strict-transport-security'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 应将 maxAge 设置为 7776000 ms(90 天)
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.match(data.headers['strict-transport-security'], /^max-age=7776000;?/); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确加载 helmet.hsts() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(data.appStack, 'hsts');
|
||||
assert.property(data.headers, 'strict-transport-security');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
应将 maxAge 设置为 7776000 ms(90 天)
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data.headers['strict-transport-security'],
|
||||
/^max-age=7776000;?/
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,47 +1,32 @@
|
||||
---
|
||||
id: 587d8248367417b2b2512c3a
|
||||
title: 使用 helment.noSniff() 来避免推断响应的 MIME 类型
|
||||
challengeType: 2
|
||||
forumTopicId: 301574
|
||||
title: 使用 helment.noSniff() 来避免推断响应的 MIME 类型
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
浏览器可以通过 content 或者 MIME 嗅探来判断不同的响应内容。这两个的优先级比 header 中的 Content-Type 高。这在一些情况下非常方便,但也会造成一定的安全风险。我们可以通过中间件来把 header 中的 X-Content-Type-Options 字段设置为 nosniff,这样浏览器就不会绕过 header 中的 Content-Type 了。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确加载 helmet.noSniff() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'nosniff'); assert.equal(data.headers['x-content-type-options'], 'nosniff'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确加载 helmet.noSniff() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(data.appStack, 'nosniff');
|
||||
assert.equal(data.headers['x-content-type-options'], 'nosniff');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
---
|
||||
id: 587d8249367417b2b2512c40
|
||||
title: 使用 helmet() 中间件来配置 Helmet
|
||||
challengeType: 2
|
||||
forumTopicId: 301575
|
||||
title: 使用 helmet() 中间件来配置 Helmet
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
`app.use(helmet())` 会自动加载除了 `noCache()` 和 `contentSecurityPolicy()` 以外的,上面所有提到的中间件。但如果需要的话,我们也可以手动加入这两个中间件。通过修改配置对象,你还可以启用或禁用其它中间件。
|
||||
|
||||
<code>app.use(helmet())</code> 会自动加载除了 <code>noCache()</code> 和 <code>contentSecurityPolicy()</code> 以外的,上面所有提到的中间件。但如果需要的话,我们也可以手动加入这两个中间件。通过修改配置对象,你还可以启用或禁用其它中间件。
|
||||
<h3>示例:</h3>
|
||||
|
||||
```js
|
||||
@ -27,40 +28,15 @@ app.use(helmet({
|
||||
}))
|
||||
```
|
||||
|
||||
为了方便教学和测试,我们会一个一个地引入中间件。但在实际项目中,通过使用父级的 <code>helmet()</code> 来实现是最清晰和简洁的。
|
||||
</section>
|
||||
为了方便教学和测试,我们会一个一个地引入中间件。但在实际项目中,通过使用父级的 `helmet()` 来实现是最清晰和简洁的。
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 没有测试—这是一个介绍关卡
|
||||
testString: assert(true)
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
没有测试—这是一个介绍关卡
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
assert(true);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,47 +1,35 @@
|
||||
---
|
||||
id: 587d8249367417b2b2512c3e
|
||||
title: 使用 helment.noCache() 禁用客户端缓存
|
||||
challengeType: 2
|
||||
forumTopicId: 301576
|
||||
title: 使用 helment.noCache() 禁用客户端缓存
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
如果你为你的网站发布了一个更新,此时你一定想要用户看到最新的版本。为此,你可以通过禁用浏览器缓存来实现。而且,这个功能在开发环境中也非常有用。但另一方面,缓存可以为你的网站带来性能方面的提升,因此你应该只在必要的时候禁用缓存。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确加载 helmet.noCache() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'nocache'); assert.equal(data.headers['cache-control'], 'no-store, no-cache, must-revalidate, proxy-revalidate'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确加载 helmet.noCache() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(data.appStack, 'nocache');
|
||||
assert.equal(
|
||||
data.headers['cache-control'],
|
||||
'no-store, no-cache, must-revalidate, proxy-revalidate'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,47 +1,32 @@
|
||||
---
|
||||
id: 587d8248367417b2b2512c3d
|
||||
title: 使用 helmet.dnsPrefetchControl() 禁用 DNS 预获取
|
||||
challengeType: 2
|
||||
forumTopicId: 301577
|
||||
title: 使用 helmet.dnsPrefetchControl() 禁用 DNS 预获取
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
为了提高性能,大部分浏览器都会为页面上的链接预先加载 DNS 记录。这样当用户点击一个链接的时候浏览器已经知道其 IP 地址了。但这也会造成 DNS 服务的过度使用(如果你有一个百万用户数量级的大型网站)、隐私问题(窃听者可以借此推测出你在访问哪个页面)、页面统计数据准确性(有些没访问过的链接会被标记成已访问)。如果你对安全性要求比较高,你应该禁用 DNS 预加载。当然,这样做会让你损失一些性能。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确加载 helmet.dnsPrefetchControl() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'dnsPrefetchControl'); assert.equal(data.headers['x-dns-prefetch-control'], 'off'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确加载 helmet.dnsPrefetchControl() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(data.appStack, 'dnsPrefetchControl');
|
||||
assert.equal(data.headers['x-dns-prefetch-control'], 'off');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
---
|
||||
id: 58a25bcff9fc0f352b528e7d
|
||||
title: 哈希和异步比较密码
|
||||
challengeType: 2
|
||||
forumTopicId: 301578
|
||||
title: 哈希和异步比较密码
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
由于哈希运算会占用很大计算机资源并且会耗费比较多时间,因此比较推荐的做法是异步调用哈希算法,这样就不会因此阻止其它连接或请求了。异步调用哈希方法非常简单,只需要:
|
||||
|
||||
```js
|
||||
@ -16,11 +17,10 @@ bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => {
|
||||
});
|
||||
```
|
||||
|
||||
</section>
|
||||
# --instructions--
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
把这段哈希方法添加到你的服务器(我们已经定义好这个方法的变量给你直接使用了),然后你可以尝试在控制台输出。之后,我们通常需要把哈希的结果保存到数据库。
|
||||
|
||||
当你需要对比用户输入的值是否和之前哈希过的值一样的时候,只需要调用对比函数:
|
||||
|
||||
```js
|
||||
@ -29,7 +29,7 @@ bcrypt.compare(myPlaintextPassword, hash, (err, res) => {
|
||||
});
|
||||
```
|
||||
|
||||
当控制台输出生成的哈希并在对比的回调中输出结果后,我们就可以将其添加到现有的哈希函数中。控制台中会首先输出一个哈希结果,然后输出 <code>true</code>。如果将比较函数中的 "myPlaintextPassword" 更改为 "someOtherPlaintextPassword",则比较的结果应显示 <code>false</code>。
|
||||
当控制台输出生成的哈希并在对比的回调中输出结果后,我们就可以将其添加到现有的哈希函数中。控制台中会首先输出一个哈希结果,然后输出 `true`。如果将比较函数中的 "myPlaintextPassword" 更改为 "someOtherPlaintextPassword",则比较的结果应显示 `false`。
|
||||
|
||||
```js
|
||||
bcrypt.hash('passw0rd!', 13, (err, hash) => {
|
||||
@ -43,34 +43,31 @@ bcrypt.hash('passw0rd!', 13, (err, hash) => {
|
||||
```
|
||||
|
||||
请在完成挑战后提交你的页面。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
# --hints--
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应异步地进行哈希并正确地执行对比
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /START_ASYNC[^]*bcrypt.hash.*myPlaintextPassword( |),( |)saltRounds( |),( |).*err( |),( |)hash[^]*END_ASYNC/gi, 'You should call bcrypt.hash on myPlaintextPassword and saltRounds and handle err and hash as a result in the callback'); assert.match(data, /START_ASYNC[^]*bcrypt.hash[^]*bcrypt.compare.*myPlaintextPassword( |),( |)hash( |),( |).*err( |),( |)res[^]*}[^]*}[^]*END_ASYNC/gi, 'Nested within the hash function should be the compare function comparing myPlaintextPassword to hash'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应异步地进行哈希并正确地执行对比
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/START_ASYNC[^]*bcrypt.hash.*myPlaintextPassword( |),( |)saltRounds( |),( |).*err( |),( |)hash[^]*END_ASYNC/gi,
|
||||
'You should call bcrypt.hash on myPlaintextPassword and saltRounds and handle err and hash as a result in the callback'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/START_ASYNC[^]*bcrypt.hash[^]*bcrypt.compare.*myPlaintextPassword( |),( |)hash( |),( |).*err( |),( |)res[^]*}[^]*}[^]*END_ASYNC/gi,
|
||||
'Nested within the hash function should be the compare function comparing myPlaintextPassword to hash'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,61 +1,58 @@
|
||||
---
|
||||
id: 58a25bcff9fc0f352b528e7e
|
||||
title: 哈希和同步比较密码
|
||||
challengeType: 2
|
||||
forumTopicId: 301579
|
||||
title: 哈希和同步比较密码
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
同步执行哈希运算是非常简单的,但这会在哈希计算量大并且次数多的情况下造成延迟。如果想要同步执行哈希运算,只需要这样调用:
|
||||
|
||||
```js
|
||||
var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);
|
||||
```
|
||||
|
||||
把同步哈希的方法添加到你的代码并在控制台输出。和之前一样,我们已经为你定义好了你需要使用的变量,你不需要做任何改动。你可能注意到,即使你对同一个密码进行哈希运算,同步哈希和异步哈希的结果也是不一样的。这是因为每次哈希的盐都是随机生成的,这种随机反映在前 22 个字符的不同上。
|
||||
当你需要对比用户输入的值是否和之前哈希过的值一样的时候,只需要调用对比函数:
|
||||
把同步哈希的方法添加到你的代码并在控制台输出。和之前一样,我们已经为你定义好了你需要使用的变量,你不需要做任何改动。你可能注意到,即使你对同一个密码进行哈希运算,同步哈希和异步哈希的结果也是不一样的。这是因为每次哈希的盐都是随机生成的,这种随机反映在前 22 个字符的不同上。 当你需要对比用户输入的值是否和之前哈希过的值一样的时候,只需要调用对比函数:
|
||||
|
||||
```js
|
||||
var result = bcrypt.compareSync(myPlaintextPassword, hash);
|
||||
```
|
||||
|
||||
返回的结果为 <code>true</code> 或 <code>false</code>
|
||||
</section>
|
||||
返回的结果为 `true` 或 `false`
|
||||
|
||||
# --instructions--
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
请添加这个方法,并把结果输出到控制台,以此来验证同步哈希操作是否成功。
|
||||
|
||||
请在完成挑战后提交你的页面。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
# --hints--
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应同步地进行哈希并正确地执行对比
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js') .then(data => { assert.match(data, /START_SYNC[^]*hash.*=.*bcrypt.hashSync.*myPlaintextPassword( |),( |)saltRounds[^]*END_SYNC/gi, 'You should call bcrypt.hashSync on myPlaintextPassword with saltRounds'); assert.match(data, /START_SYNC[^]*result.*=.*bcrypt.compareSync.*myPlaintextPassword( |),( |)hash[^]*END_SYNC/gi, 'You should call bcrypt.compareSync on myPlaintextPassword with the hash generated in the last line'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应同步地进行哈希并正确地执行对比
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/START_SYNC[^]*hash.*=.*bcrypt.hashSync.*myPlaintextPassword( |),( |)saltRounds[^]*END_SYNC/gi,
|
||||
'You should call bcrypt.hashSync on myPlaintextPassword with saltRounds'
|
||||
);
|
||||
assert.match(
|
||||
data,
|
||||
/START_SYNC[^]*result.*=.*bcrypt.compareSync.*myPlaintextPassword( |),( |)hash[^]*END_SYNC/gi,
|
||||
'You should call bcrypt.compareSync on myPlaintextPassword with the hash generated in the last line'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,47 +1,32 @@
|
||||
---
|
||||
id: 587d8247367417b2b2512c37
|
||||
title: 使用 helmet.hidePoweredBy() 隐藏潜在的危险信息
|
||||
challengeType: 2
|
||||
forumTopicId: 301580
|
||||
title: 使用 helmet.hidePoweredBy() 隐藏潜在的危险信息
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
如果黑客发现你的网站是用 Express 搭建的,那么他们就可以利用 Express 或 Node 现存的漏洞来攻击你的网站。<code>X-Powered-By: Express</code> 默认情况下会添加到所有响应的 header。不过 <code>helmet.hidePoweredBy()</code> 中间件可以帮你把 header 中的 X-Powered-By 字段移除。你甚至可以把它设置成其它的值来骗过黑客,比如 <code>app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }))</code>
|
||||
</section>
|
||||
# --description--
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
</section>
|
||||
如果黑客发现你的网站是用 Express 搭建的,那么他们就可以利用 Express 或 Node 现存的漏洞来攻击你的网站。`X-Powered-By: Express` 默认情况下会添加到所有响应的 header。不过 `helmet.hidePoweredBy()` 中间件可以帮你把 header 中的 X-Powered-By 字段移除。你甚至可以把它设置成其它的值来骗过黑客,比如 `app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }))`
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
# --hints--
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确地加载 helmet.hidePoweredBy() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'hidePoweredBy'); assert.notEqual(data.headers['x-powered-by'], 'Express')}, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确地加载 helmet.hidePoweredBy() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(data.appStack, 'hidePoweredBy');
|
||||
assert.notEqual(data.headers['x-powered-by'], 'Express');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,48 +1,36 @@
|
||||
---
|
||||
id: 587d8247367417b2b2512c36
|
||||
title: 安装和引入 Helmet
|
||||
challengeType: 2
|
||||
forumTopicId: 301581
|
||||
title: 安装和引入 Helmet
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
Helmet 通过配置不同的 HTTP header 信息来使你的 Express 应用更加安全。
|
||||
</section>
|
||||
# --description--
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
Helmet 通过配置不同的 HTTP header 信息来使你的 Express 应用更加安全。
|
||||
|
||||
# --instructions--
|
||||
|
||||
安装版本号为 `3.21.3` 的 Helmet,并引入它。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
# --hints--
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: '<code>package.json</code> 中应存在版本号为 <code>3.21.3</code> 的 <code>helmet</code> 依赖项'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/package.json').then(data => { var packJson = JSON.parse(data); assert(packJson.dependencies.helmet === '3.21.3' ); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
`package.json` 中应存在版本号为 `3.21.3` 的 `helmet` 依赖项
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert(packJson.dependencies.helmet === '3.21.3');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,50 +1,54 @@
|
||||
---
|
||||
id: 587d8247367417b2b2512c38
|
||||
title: 使用 helmet.frameguard() 降低点击劫持的风险
|
||||
challengeType: 2
|
||||
forumTopicId: 301582
|
||||
title: 使用 helmet.frameguard() 降低点击劫持的风险
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
黑客可能会不经过你的允许,把你的页面嵌套在 <code><frame></code> 或者 <code><iframe></code> 标签里,用以实现“点击劫持”。点击劫持是一种视觉上的欺骗手段,让用户误以为自己在与所看到的网页交互。通过 iframe,黑客可以在你的页面上添加一个透明的“层”,然后把自己的恶意代码放在一个用户看不到的按钮中。这样一来,你网站的执行环境就被黑客设置成了他想要的效果。helmet 中间件可以设置 header 中的 X-Frame-Options 字段,这样我们就能设置哪些人才可以通过 frame/iframe 引入我们的页面了。这个配置有三个选项:DENY、SAMEORIGIN、ALLOW-FROM。
|
||||
在这个挑战中,我们的应用不需要被 iframe 引用。
|
||||
</section>
|
||||
# --description--
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
请调用 <code>helmet.frameguard()</code>,并传入配置对象 <code>{action: 'deny'}</code>。
|
||||
</section>
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
黑客可能会不经过你的允许,把你的页面嵌套在 \``或者`</code> 标签里,用以实现“点击劫持”。点击劫持是一种视觉上的欺骗手段,让用户误以为自己在与所看到的网页交互。通过 iframe,黑客可以在你的页面上添加一个透明的“层”,然后把自己的恶意代码放在一个用户看不到的按钮中。这样一来,你网站的执行环境就被黑客设置成了他想要的效果。helmet 中间件可以设置 header 中的 X-Frame-Options 字段,这样我们就能设置哪些人才可以通过 frame/iframe 引入我们的页面了。这个配置有三个选项:DENY、SAMEORIGIN、ALLOW-FROM。 在这个挑战中,我们的应用不需要被 iframe 引用。 </section>\`
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确加载 helmet.frameguard() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'frameguard', 'helmet.frameguard() middleware is not mounted correctly'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 应将 helmet.frameguard() 中的 action 值设置为 'DENY'
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.property(data.headers, 'x-frame-options'); assert.equal(data.headers['x-frame-options'], 'DENY');}, xhr => { throw new Error(xhr.responseText); })
|
||||
# --instructions--
|
||||
|
||||
```
|
||||
请调用 `helmet.frameguard()`,并传入配置对象 `{action: 'deny'}`。
|
||||
|
||||
</section>
|
||||
# --hints--
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确加载 helmet.frameguard() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(
|
||||
data.appStack,
|
||||
'frameguard',
|
||||
'helmet.frameguard() middleware is not mounted correctly'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
应将 helmet.frameguard() 中的 action 值设置为 'DENY'
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.property(data.headers, 'x-frame-options');
|
||||
assert.equal(data.headers['x-frame-options'], 'DENY');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,51 +1,40 @@
|
||||
---
|
||||
id: 587d8247367417b2b2512c39
|
||||
title: 使用 helmet.xssFilter() 降低跨站点脚本(XSS)攻击的风险
|
||||
challengeType: 2
|
||||
forumTopicId: 301583
|
||||
title: 使用 helmet.xssFilter() 降低跨站点脚本(XSS)攻击的风险
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
跨站脚本攻击(XSS)是一种比较常见的攻击手段,通过给页面注入恶意脚本来获取用户的 session cookie 和密码等信息。
|
||||
防止这类型的攻击也非常简单:“永远不要相信用户的输入”。作为一名开发人员,你一定要对所有外部的输入进行审查,其中包括来自表单、GET 请求的 URL、POST 请求主体的内容等。审核的意思是,你需要查找并给有潜在风险的字符进行编码,比如 < 和 >
|
||||
|
||||
防止这类型的攻击也非常简单:“永远不要相信用户的输入”。作为一名开发人员,你一定要对所有外部的输入进行审查,其中包括来自表单、GET 请求的 URL、POST 请求主体的内容等。审核的意思是,你需要查找并给有潜在风险的字符进行编码,比如 < 和 >
|
||||
|
||||
现代浏览器中已经存在有效的策略来应对这一类问题。通常是通过配置 HTTP 请求的 header 来实现。
|
||||
|
||||
HTTP header 的 X-XSS-Protection 字段就可以为我们提供最基本的保护。浏览器通过启发式过滤法则来检测有潜在风险的注入脚本,如果这个 header 字段被启用,浏览器就会改变脚本中存在风险的代码,从而使恶意代码不再生效。
|
||||
|
||||
但是浏览器对这个功能的支持十分有限。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确加载 helmet.xssFilter() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'xXssProtection'); assert.property(data.headers, 'x-xss-protection'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确加载 helmet.xssFilter() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(data.appStack, 'xXssProtection');
|
||||
assert.property(data.headers, 'x-xss-protection');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,47 +1,32 @@
|
||||
---
|
||||
id: 587d8248367417b2b2512c3b
|
||||
title: 使用 helment.ieNoOpen() 防止 IE 打开不受信任的 HTML
|
||||
challengeType: 2
|
||||
forumTopicId: 301584
|
||||
title: 使用 helment.ieNoOpen() 防止 IE 打开不受信任的 HTML
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
有些网站会下载不安全的 HTML 文件,某些版本的 IE 默认情况下还会在你网站的作用域下打开这些 HTML 文件。换句话说,这些不安全的 HTML 页面可以在你的网站做恶意行为。我们可以通过中间件来设置 header 中的 X-Download-Options 字段,让它的值为 noopen。这样就可以防止 IE 在不信任的网站下执行下载的文件。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确加载 helmet.ieNoOpen() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'ienoopen'); assert.equal(data.headers['x-download-options'], 'noopen'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确加载 helmet.ieNoOpen() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(data.appStack, 'ienoopen');
|
||||
assert.equal(data.headers['x-download-options'], 'noopen');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,51 +1,63 @@
|
||||
---
|
||||
id: 587d8249367417b2b2512c3f
|
||||
title: 使用 helment.contentSecurityPolicy() 设置内容安全策略
|
||||
challengeType: 2
|
||||
forumTopicId: 301585
|
||||
title: 使用 helment.contentSecurityPolicy() 设置内容安全策略
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
在这个挑战中,我们要重点讨论现代浏览器中一种有效并且能大幅度减轻安全风险和很多种类型常见攻击的安全防护。通过配置内容安全策略,你可以防止很多类型的脚本恶意注入。这会让你的应用远离 XSS 漏洞、恶意追踪、恶意 frames 和很多其他攻击。CSP 通过配置资源白名单来避免这些问题。你可以给任何一种类型的页面资源(脚本、样式文件、字体、frames、媒体文件等)做这个配置。它支持很多指令,所以网站管理员可以做细致的控制。更多详情请参考 HTML 5 Rocks 和 KeyCDN。不幸的是,一些旧的浏览器不支持 CSP。
|
||||
|
||||
默认的指令很容易受到攻击, 所以设置 defaultSrc 指令作为降级方案很重要。Helmet 同时支持 defaultSrc 和 default-src 命名规范。降级方案可以应用在大部分指令上。
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
在这个练习中,我们使用 <code>helmet.contentSecurityPolicy()</code> 并配置 <code>defaultSrc</code> 为 <code>["self"]</code>(允许的资源列表必须在一个数组当中)。这样做表示只信任自己网站的域名。另外,配置 <code>scriptSrc</code> 指令可以限制脚本只能本网站域名上或者信任的域名 'trusted-cdn.com' 上下载。
|
||||
提示: 在 <code>self</code> 关键词中, 单引号也是关键词的一部分, 所以你应该用双引号来包起它才能正常工作。
|
||||
</section>
|
||||
# --instructions--
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
在这个练习中,我们使用 `helmet.contentSecurityPolicy()` 并配置 `defaultSrc` 为 `["self"]`(允许的资源列表必须在一个数组当中)。这样做表示只信任自己网站的域名。另外,配置 `scriptSrc` 指令可以限制脚本只能本网站域名上或者信任的域名 'trusted-cdn.com' 上下载。
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应正确加载 helmet.csp() 中间件
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'csp'); }, xhr => { throw new Error(xhr.responseText); })
|
||||
- text: 你的 csp 配置不正确,defaultSrc 应为 ["'self'"] 并且 scriptSrc 应为 ["'self'", 'trusted-cdn.com']
|
||||
testString: getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { var cspHeader = Object.keys(data.headers).filter(function(k){ return k === 'content-security-policy' || k === 'x-webkit-csp' || k === 'x-content-security-policy' })[0]; assert.equal(data.headers[cspHeader], "default-src 'self'; script-src 'self' trusted-cdn.com"); }, xhr => { throw new Error(xhr.responseText); })
|
||||
提示: 在 `self` 关键词中, 单引号也是关键词的一部分, 所以你应该用双引号来包起它才能正常工作。
|
||||
|
||||
```
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应正确加载 helmet.csp() 中间件
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
assert.include(data.appStack, 'csp');
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
你的 csp 配置不正确,defaultSrc 应为 ["'self'"] 并且 scriptSrc 应为 ["'self'", 'trusted-cdn.com']
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/app-info').then(
|
||||
(data) => {
|
||||
var cspHeader = Object.keys(data.headers).filter(function (k) {
|
||||
return (
|
||||
k === 'content-security-policy' ||
|
||||
k === 'x-webkit-csp' ||
|
||||
k === 'x-content-security-policy'
|
||||
);
|
||||
})[0];
|
||||
assert.equal(
|
||||
data.headers[cspHeader],
|
||||
"default-src 'self'; script-src 'self' trusted-cdn.com"
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.responseText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@ -1,51 +1,62 @@
|
||||
---
|
||||
id: 58a25bcef9fc0f352b528e7c
|
||||
title: 了解 BCrypt 的哈希加密
|
||||
challengeType: 2
|
||||
forumTopicId: 301586
|
||||
title: 了解 BCrypt 的哈希加密
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
请注意,本项目在 <a href="https://repl.it/github/freeCodeCamp/boilerplate-infosec">这个 Repl.it 项目</a> 的基础上进行开发。你也可以从 <a href='https://github.com/freeCodeCamp/boilerplate-infosec/'>GitHub</a> 上克隆。
|
||||
BCrypt 哈希非常安全。哈希其实就是原数据的加密形式,结果唯一;通过把原始数据放进哈希算法然后返回固定长度的字符串实现。为了让这个过程更加复杂和安全,你还可以加你的哈希添加 <em>盐</em>。加盐其实就是给在哈希算法执行前给源数据添加随机的字符串数据,这会让破解哈希结果更加复杂
|
||||
BCrypt 的哈希结果会是这样 <code>$2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>。它遵循固定的结构,前面一小节 <code>$2a</code> 说明了该哈希采用什么算法。下一部分 <code>$13</code> 定义了 <em>cost</em>。cost 就是生成此哈希结果所用到的资源数量级;它是 2^cost 的对数,可以表说单位时间内数据放入哈希算法的次数。举个例子,cost 为 10 的意思就是说你能够在一个普通的计算机上每秒钟计算出 10 个密码的哈希结果。然而,如果 cost 为 15,那计算每个哈希结果就要 3 秒钟。再举例子,如果 cost 是 31,那每次哈希运算需要话费好几天才能完成。通常 cost 为 12 的哈希运算就已经足够安全。哈希结果的最后一部分 <code>$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code> 看起来像是由随机数字、点和字母组成的字符串,但实际上它有两部分内容。前面 22 个字符是加入的盐,剩下的就是加密过的密码!
|
||||
</section>
|
||||
# --description--
|
||||
|
||||
请注意,本项目在 [这个 Repl.it 项目](https://repl.it/github/freeCodeCamp/boilerplate-infosec) 的基础上进行开发。你也可以从 [GitHub](https://github.com/freeCodeCamp/boilerplate-infosec/) 上克隆。
|
||||
|
||||
BCrypt 哈希非常安全。哈希其实就是原数据的加密形式,结果唯一;通过把原始数据放进哈希算法然后返回固定长度的字符串实现。为了让这个过程更加复杂和安全,你还可以加你的哈希添加 *盐*。加盐其实就是给在哈希算法执行前给源数据添加随机的字符串数据,这会让破解哈希结果更加复杂
|
||||
|
||||
BCrypt 的哈希结果会是这样 `$2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm`。它遵循固定的结构,前面一小节 `$2a` 说明了该哈希采用什么算法。下一部分 `$13` 定义了 *cost*。cost 就是生成此哈希结果所用到的资源数量级;它是 2^cost 的对数,可以表说单位时间内数据放入哈希算法的次数。举个例子,cost 为 10 的意思就是说你能够在一个普通的计算机上每秒钟计算出 10 个密码的哈希结果。然而,如果 cost 为 15,那计算每个哈希结果就要 3 秒钟。再举例子,如果 cost 是 31,那每次哈希运算需要话费好几天才能完成。通常 cost 为 12 的哈希运算就已经足够安全。哈希结果的最后一部分 `$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm` 看起来像是由随机数字、点和字母组成的字符串,但实际上它有两部分内容。前面 22 个字符是加入的盐,剩下的就是加密过的密码!
|
||||
|
||||
# --instructions--
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
要开始使用 BCrypt, 只需将它添加到你的依赖列表,然后在你的服务器引入 'bcrypt'。
|
||||
|
||||
请在完成挑战后提交你的页面。
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
# --hints--
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 应添加 BCyrpt 至依赖列表
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/package.json') .then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'bcrypt', 'Your project should list "bcrypt" as a dependency'); }, xhr => { throw new Error(xhr.statusText); })
|
||||
- text: 应正确引入 BCrypt
|
||||
testString: getUserInput => $.get(getUserInput('url')+ '/_api/server.js').then(data => {assert.match(data, /bcrypt.*=.*require.*('|")bcrypt('|")/gi, 'You should correctly require and instantiate socket.io as io.');}, xhr => { throw new Error(xhr.statusText); })
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
应添加 BCyrpt 至依赖列表
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/package.json').then(
|
||||
(data) => {
|
||||
var packJson = JSON.parse(data);
|
||||
assert.property(
|
||||
packJson.dependencies,
|
||||
'bcrypt',
|
||||
'Your project should list "bcrypt" as a dependency'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
</section>
|
||||
应正确引入 BCrypt
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
$.get(getUserInput('url') + '/_api/server.js').then(
|
||||
(data) => {
|
||||
assert.match(
|
||||
data,
|
||||
/bcrypt.*=.*require.*('|")bcrypt('|")/gi,
|
||||
'You should correctly require and instantiate socket.io as io.'
|
||||
);
|
||||
},
|
||||
(xhr) => {
|
||||
throw new Error(xhr.statusText);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
Reference in New Issue
Block a user