feat(i18n/Chinese): add translation of Information Security with HelmetJS (2/4) (#39540)

This commit is contained in:
Xing Liu
2020-09-17 03:53:22 -07:00
committed by GitHub
parent 206c5994b7
commit 7865d0d0f0
14 changed files with 286 additions and 108 deletions

View File

@ -2,15 +2,20 @@
id: 587d8248367417b2b2512c3c
title: Ask Browsers to Access Your Site via HTTPS Only with helmet.hsts()
challengeType: 2
videoUrl: ''
forumTopicId: 301573
localeTitle: 要求浏览器通过HTTPS访问您的站点仅限于使用helmet.hsts
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。 HTTP严格传输安全HSTS是一种Web安全策略可帮助保护网站免受协议降级攻击和cookie劫持。如果您的网站可以通过HTTPS访问您可以要求用户的浏览器避免使用不安全的HTTP。通过设置标头Strict-Transport-Security您可以告诉浏览器在指定的时间内对将来的请求使用HTTPS。这将适用于初始请求之后的请求。配置helmet.hsts以在接下来的90天内使用HTTPS。传递配置对象{maxAgetimeInSecondsforcetrue}。 Glitch已经启用了hsts。要覆盖其设置您需要在配置对象中将字段“force”设置为true。在检查Glitch标头进行测试后我们将拦截并恢复Glitch标头。注意在自定义网站上配置HTTPS需要获取域和SSL / TSL证书。 </section>
<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>
## Instructions
<section id="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
@ -18,9 +23,9 @@ localeTitle: 要求浏览器通过HTTPS访问您的站点仅限于使用helmet.h
```yml
tests:
- text: helmet.hsts()中间件应正确安装
- 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毫秒90天
- text: 应将 maxAge 设置为 7776000 ms90 天)
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); })
```
@ -36,7 +41,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,19 @@
id: 587d8248367417b2b2512c3a
title: Avoid Inferring the Response MIME Type with helmet.noSniff()
challengeType: 2
videoUrl: ''
localeTitle: 避免使用helmet.noSniff推断响应MIME类型
forumTopicId: 301574
localeTitle: 使用 helment.noSniff() 来避免推断响应MIME 类型
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。浏览器可以使用内容或MIME嗅探来适应来自响应的不同数据类型。它们覆盖Content-Type标头以猜测和处理数据。虽然这在某些情况下可能很方便但它也可能导致一些危险的攻击。此中间件将X-Content-Type-Options标头设置为nosniff。这指示浏览器不绕过提供的Content-Type。 </section>
<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> 上克隆。
浏览器可以通过 content 或者 MIME 嗅探来判断不同的响应内容。这两个的优先级比 header 中的 Content-Type 高。这在一些情况下非常方便,但也会造成一定的安全风险。我们可以通过中间件来把 header 中的 X-Content-Type-Options 字段设置为 nosniff这样浏览器就不会绕过 header 中的 Content-Type 了。
</section>
## Instructions
<section id="instructions">
<section id='instructions'>
</section>
## Tests
@ -18,7 +22,7 @@ localeTitle: 避免使用helmet.noSniff推断响应MIME类型
```yml
tests:
- text: helmet.noSniff()中间件应该正确安装
- 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); })
```
@ -34,7 +38,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,38 @@
id: 587d8249367417b2b2512c40
title: Configure Helmet Using the parent helmet() Middleware
challengeType: 2
videoUrl: ''
localeTitle: 配置头盔使用“父”头盔()中间件
forumTopicId: 301575
localeTitle: 使用 helmet() 中间件来配置 Helmet
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。 app.usehelmet将自动包含上面介绍的所有中间件noCache和contentSecurityPolicy除外但如果需要可以启用这些中间件。您还可以使用配置对象单独禁用或配置任何其他中间件。 //示例<code>app.use(helmet({</code> <code>frameguard: { // configure</code> <code>action: &#39;deny&#39;</code> <code>},</code> <code>contentSecurityPolicy: { // enable and configure</code> <code>directives: {</code> <code>defaultSrc: [&quot;self&quot;],</code> <code>styleSrc: [&#39;style.com&#39;],</code> <code>}</code> <code>},</code> <code>dnsPrefetchControl: false // disable</code> <code>}))</code>我们分别为教学目的引入了每个中间件,并且易于测试。使用“父”头盔()中间件对于真实项目来说是最简单,更清洁的。 </section>
<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>app.use(helmet())</code> 会自动加载除了 <code>noCache()</code><code>contentSecurityPolicy()</code> 以外的,上面所有提到的中间件。但如果需要的话,我们也可以手动加入这两个中间件。通过修改配置对象,你还可以启用或禁用其它中间件。
<h3>示例:</h3>
```js
app.use(helmet({
frameguard: { // 配置
action: 'deny'
},
contentSecurityPolicy: { // 启用并配置
directives: {
defaultSrc: ["self"],
styleSrc: ['style.com'],
}
},
dnsPrefetchControl: false // 禁用
}))
```
为了方便教学和测试,我们会一个一个地引入中间件。但在实际项目中,通过使用父级的 <code>helmet()</code> 来实现是最清晰和简洁的。
</section>
## Instructions
<section id="instructions">
<section id='instructions'>
</section>
## Tests
@ -18,7 +41,7 @@ localeTitle: 配置头盔使用“父”头盔()中间件
```yml
tests:
- text: 没有测试 - 这是一个描述性的挑战
- text: 没有测试这是一个介绍关卡
testString: assert(true)
```
@ -34,7 +57,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,19 @@
id: 587d8249367417b2b2512c3e
title: Disable Client-Side Caching with helmet.noCache()
challengeType: 2
videoUrl: ''
localeTitle: 使用helmet.noCache禁用客户端缓存
forumTopicId: 301576
localeTitle: 使用 helment.noCache() 禁用客户端缓存
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。如果您要为您的网站发布更新,并且您希望用户始终下载较新的版本,您可以(尝试)在客户端的浏览器上禁用缓存。它在开发中也很有用。缓存具有性能优势,您将失去这些优势,因此只有在真正需要时才使用此选项。 </section>
<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> 上克隆。
如果你为你的网站发布了一个更新,此时你一定想要用户看到最新的版本。为此,你可以通过禁用浏览器缓存来实现。而且,这个功能在开发环境中也非常有用。但另一方面,缓存可以为你的网站带来性能方面的提升,因此你应该只在必要的时候禁用缓存。
</section>
## Instructions
<section id="instructions">
<section id='instructions'>
</section>
## Tests
@ -18,7 +22,7 @@ localeTitle: 使用helmet.noCache禁用客户端缓存
```yml
tests:
- text: 正确安装helmet.noCache中间件
- 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); })
```
@ -34,7 +38,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,19 @@
id: 587d8248367417b2b2512c3d
title: Disable DNS Prefetching with helmet.dnsPrefetchControl()
challengeType: 2
videoUrl: ''
localeTitle: 使用helmet.dnsPrefetchControl禁用DNS
forumTopicId: 301577
localeTitle: 使用 helmet.dnsPrefetchControl() 禁用 DNS 预获
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。为了提高性能大多数浏览器都会为页面中的链接预取DNS记录。以这种方式当用户点击链接时目标IP已知。这可能导致过度使用DNS服务如果您拥有一个大网站数百万人访问......隐私问题一个窃听者可能会推断您在某个页面上或者页面统计信息更改某些链接可能会即使他们不是也会出现。如果您有高安全性需求则可以以性能损失为代价禁用DNS预取。 </section>
<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> 上克隆。
为了提高性能,大部分浏览器都会为页面上的链接预先加载 DNS 记录。这样当用户点击一个链接的时候浏览器已经知道其 IP 地址了。但这也会造成 DNS 服务的过度使用(如果你有一个百万用户数量级的大型网站)、隐私问题(窃听者可以借此推测出你在访问哪个页面)、页面统计数据准确性(有些没访问过的链接会被标记成已访问)。如果你对安全性要求比较高,你应该禁用 DNS 预加载。当然,这样做会让你损失一些性能。
</section>
## Instructions
<section id="instructions">
<section id='instructions'>
</section>
## Tests
@ -18,7 +22,7 @@ localeTitle: 使用helmet.dnsPrefetchControl禁用DNS预取
```yml
tests:
- text: 正确安装helmet.dnsPrefetchControl中间件
- 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); })
```
@ -34,7 +38,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,20 +2,48 @@
id: 58a25bcff9fc0f352b528e7d
title: Hash and Compare Passwords Asynchronously
challengeType: 2
videoUrl: ''
forumTopicId: 301578
localeTitle: 哈希和异步比较密码
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-bcrypt/">GitHub</a>克隆的。由于散列设计为计算密集型,因此建议在服务器上异步执行此操作,以避免在散列时阻止传入连接。所有你需要做的就是异步散列密码是调用<code>bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) =&gt; { /*Store hash in your db*/ });</code> <hr>将此散列函数添加到服务器(我们已经定义了函数中使用的变量供您使用)并将其记录到控制台以供您查看!此时,您通常会将哈希值保存到数据库中。现在,当您需要确定新输入是否与散列相同时,您只需使用比较函数<code>bcrypt.compare(myPlaintextPassword, hash, (err, res) =&gt; { /*res == true or false*/ });</code> 。在记录完成的哈希并将“res”记录到比较中的控制台之后将其添加到现有哈希函数中因为您需要在调用比较函数之前等待哈希完成。您应该在控制台中看到一个哈希值然后打印出“true”如果您将compare函数中的&#39;myPlaintextPassword&#39;更改为&#39;someOtherPlaintextPassword&#39;那么它应该为false。 <pre> bcrypt.hash&#39;passw0rd&#39;13错误哈希=&gt; {
的console.log散列; //$2a$12$Y.PHPE15wR25qrrtgGkiYe2sXo98cjuMCG1YwSI5rJW1DSJp0gEYS
bcrypt.compare&#39;passw0rd&#39;hasherrres=&gt; {
的console.logRES; //真正
};
}; </pre>当您认为自己已经做对时,请提交您的页面。 </section>
<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> 上克隆。
由于哈希运算会占用很大计算机资源并且会耗费比较多时间,因此比较推荐的做法是异步调用哈希算法,这样就不会因此阻止其它连接或请求了。异步调用哈希方法非常简单,只需要:
```js
bcrypt.hash(myPlaintextPassword, saltRounds, (err, hash) => {
/* 在数据库中存储 hash */
});
```
</section>
## Instructions
<section id="instructions">
<section id='instructions'>
把这段哈希方法添加到你的服务器(我们已经定义好这个方法的变量给你直接使用了),然后你可以尝试在控制台输出。之后,我们通常需要把哈希的结果保存到数据库。
当你需要对比用户输入的值是否和之前哈希过的值一样的时候,只需要调用对比函数:
```js
bcrypt.compare(myPlaintextPassword, hash, (err, res) => {
/*res == true or false*/
});
```
当控制台输出生成的哈希并在对比的回调中输出结果后,我们就可以将其添加到现有的哈希函数中。控制台中会首先输出一个哈希结果,然后输出 <code>true</code>。如果将比较函数中的 "myPlaintextPassword" 更改为 "someOtherPlaintextPassword",则比较的结果应显示 <code>false</code>
```js
bcrypt.hash('passw0rd!', 13, (err, hash) => {
console.log(hash);
//$2a$12$Y.PHPE15wR25qrrtgGkiYe2sXo98cjuMCG1YwSI5rJW1DSJp0gEYS
bcrypt.compare('passw0rd!', hash, (err, res) => {
console.log(res); //true
});
});
```
请在完成挑战后提交你的页面。
</section>
## Tests
@ -23,7 +51,7 @@ localeTitle: 哈希和异步比较密码
```yml
tests:
- text: 异步哈希生成并正确比较
- 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); })
```
@ -39,7 +67,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,33 @@
id: 58a25bcff9fc0f352b528e7e
title: Hash and Compare Passwords Synchronously
challengeType: 2
videoUrl: ''
localeTitle: 哈希同步比较密码
forumTopicId: 301579
localeTitle: 哈希同步比较密码
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-bcrypt/">GitHub</a>克隆的。同步散列也很容易,但如果使用服务器端成本高或经常进行散列会导致延迟。使用此方法进行散列与调用<code>var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);</code> <hr>将此散列方法添加到代码中,然后将结果记录到控制台。同样,使用的变量已在服务器中定义,因此您无需调整它们。您可能会注意到即使您使用与异步函数相同的密码进行哈希处理,控制台中的结果也不同 - 这是由于每次哈希值随机生成如第三个哈希字符串中的前22个字符所示。现在将密码输入与新的同步散列进行比较您将使用compareSync方法 <code>var result = bcrypt.compareSync(myPlaintextPassword, hash);</code>结果是布尔值true或false。添加此功能并登录控制台结果以查看其是否正常工作。当您认为自己已经做对时请提交您的页面。如果您在这些挑战期间遇到错误可以在<a href="https://gist.github.com/JosephLivengood/9a2698fb63e42d9d8b4b84235c08b4c4">此处</a>查看示例完成的代码。 </section>
<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> 上克隆。
同步执行哈希运算是非常简单的,但这会在哈希计算量大并且次数多的情况下造成延迟。如果想要同步执行哈希运算,只需要这样调用:
```js
var hash = bcrypt.hashSync(myPlaintextPassword, saltRounds);
```
把同步哈希的方法添加到你的代码并在控制台输出。和之前一样,我们已经为你定义好了你需要使用的变量,你不需要做任何改动。你可能注意到,即使你对同一个密码进行哈希运算,同步哈希和异步哈希的结果也是不一样的。这是因为每次哈希的盐都是随机生成的,这种随机反映在前 22 个字符的不同上。
当你需要对比用户输入的值是否和之前哈希过的值一样的时候,只需要调用对比函数:
```js
var result = bcrypt.compareSync(myPlaintextPassword, hash);
```
返回的结果为 <code>true</code><code>false</code>
</section>
## Instructions
<section id="instructions">
<section id='instructions'>
请添加这个方法,并把结果输出到控制台,以此来验证同步哈希操作是否成功。
请在完成挑战后提交你的页面。
</section>
## Tests
@ -18,7 +36,7 @@ localeTitle: 哈希并同步比较密码
```yml
tests:
- text: 同步哈希生成并正确比较
- 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); })
```
@ -34,7 +52,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,19 @@
id: 587d8247367417b2b2512c37
title: Hide Potentially Dangerous Information Using helmet.hidePoweredBy()
challengeType: 2
videoUrl: ''
localeTitle: 使用helmet.hidePoweredBy隐藏潜在的危险信息
forumTopicId: 301580
localeTitle: 使用 helmet.hidePoweredBy() 隐藏潜在的危险信息
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。如果黑客看到您的网站由Express提供支持他们可以利用Express / Node中的已知漏洞。 X-Powered-ByExpress默认发送来自Express的每个请求。 helmet.hidePoweredBy中间件将删除X-Powered-By标头。您还可以将标头显式设置为其他内容以便让人们离开。例如app.usehelmet.hidePoweredBy{setTo&#39;PHP 4.2.0&#39;} </section>
<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>
## Instructions
<section id="instructions">
<section id='instructions'>
</section>
## Tests
@ -18,7 +22,7 @@ localeTitle: 使用helmet.hidePoweredBy隐藏潜在的危险信息
```yml
tests:
- text: helmet.hidePoweredBy()中间件应正确安装
- 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); })
```
@ -34,7 +38,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,20 @@
id: 587d8247367417b2b2512c36
title: Install and Require Helmet
challengeType: 2
videoUrl: ''
localeTitle: 安装并需要头盔
forumTopicId: 301581
localeTitle: 安装和引入 Helmet
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。 Helmet通过设置各种HTTP标头来帮助您保护Express应用程序。安装包然后需要它。 </section>
<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>
## Instructions
<section id="instructions">
<section id='instructions'>
安装版本号为 `3.21.3` 的 Helmet并引入它。
</section>
## Tests
@ -18,8 +23,8 @@ localeTitle: 安装并需要头盔
```yml
tests:
- text: “helmet”依赖应该在package.json中
testString: getUserInput => $.get(getUserInput('url') + '/_api/package.json').then(data => { var packJson = JSON.parse(data); assert.property(packJson.dependencies, 'helmet'); }, xhr => { throw new Error(xhr.responseText); })
- 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); })
```
@ -34,7 +39,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,20 @@
id: 587d8247367417b2b2512c38
title: Mitigate the Risk of Clickjacking with helmet.frameguard()
challengeType: 2
videoUrl: ''
localeTitle: 使用helmet.frameguard降低点击劫持的风险
forumTopicId: 301582
localeTitle: 使用 helmet.frameguard() 降低点击劫持的风险
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。未经您的同意,您的页面可以放在<code>&lt;frame&gt;</code><code>&lt;iframe&gt;</code> 。除其他外这可能导致点击劫持攻击。点击劫持是一种欺骗用户与不同于用户认为的页面进行交互的技术。这可以通过iframing在恶意上下文中执行您的页面获得。在这种情况下黑客可以在页面上放置隐藏层。隐藏按钮可用于运行错误的脚本。此中间件设置X-Frame-Options标头。它限制了谁可以将您的网站放在框架中。它有三种模式DENYSAMEORIGIN和ALLOW-FROM。我们不需要我们的应用程序框架。您应该使用配置对象<code>{action: &#39;deny&#39;}</code>传递的<code>helmet.frameguard()</code></section>
<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>
## Instructions
<section id="instructions">
<section id='instructions'>
请调用 <code>helmet.frameguard()</code>,并传入配置对象 <code>{action: 'deny'}</code>
</section>
## Tests
@ -18,9 +23,9 @@ localeTitle: 使用helmet.frameguard降低点击劫持的风险
```yml
tests:
- text: helmet.frameguard()中间件应正确安装
- 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'
- 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); })
```
@ -36,7 +41,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,23 @@
id: 587d8247367417b2b2512c39
title: Mitigate the Risk of Cross Site Scripting (XSS) Attacks with helmet.xssFilter()
challengeType: 2
videoUrl: ''
localeTitle: 使用helmet.xssFilter降低跨站点脚本XSS攻击的风险
forumTopicId: 301583
localeTitle: 使用 helmet.xssFilter() 降低跨站点脚本XSS攻击的风险
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。跨站点脚本XSS是一种常见的攻击类型其中恶意脚本被注入易受攻击的页面目的是窃取会话cookie或密码等敏感数据。降低XSS攻击风险的基本规则很简单“绝不信任用户的输入”。作为开发人员您应该始终清理来自外部的所有输入。这包括来自表单GET查询URL甚至来自POST主体的数据。消毒意味着您应该找到并编码可能有危险的字符例如&lt;&gt;。现代浏览器可以通过采用更好的软件策略来帮助降低风险。通常这些都可以通过http标头进行配置。 X-XSS-Protection HTTP标头是一种基本保护。浏览器使用启发式过滤器检测潜在的注入脚本。如果启用了标头则浏览器会更改脚本代码从而中和它。它仍然有限的支持。 </section>
<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> 上克隆。
跨站脚本攻击XSS是一种比较常见的攻击手段通过给页面注入恶意脚本来获取用户的 session cookie 和密码等信息。
防止这类型的攻击也非常简单“永远不要相信用户的输入”。作为一名开发人员你一定要对所有外部的输入进行审查其中包括来自表单、GET 请求的 URL、POST 请求主体的内容等。审核的意思是,你需要查找并给有潜在风险的字符进行编码,比如 < >
现代浏览器中已经存在有效的策略来应对这一类问题。通常是通过配置 HTTP 请求的 header 来实现。
HTTP header 的 X-XSS-Protection 字段就可以为我们提供最基本的保护。浏览器通过启发式过滤法则来检测有潜在风险的注入脚本,如果这个 header 字段被启用,浏览器就会改变脚本中存在风险的代码,从而使恶意代码不再生效。
但是浏览器对这个功能的支持十分有限。
</section>
## Instructions
<section id="instructions">
<section id='instructions'>
</section>
## Tests
@ -18,7 +26,7 @@ localeTitle: 使用helmet.xssFilter降低跨站点脚本XSS攻击的
```yml
tests:
- text: helmet.xssFilter()中间件应该正确安装
- 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); })
```
@ -34,7 +42,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,19 @@
id: 587d8248367417b2b2512c3b
title: Prevent IE from Opening Untrusted HTML with helmet.ieNoOpen()
challengeType: 2
videoUrl: ''
localeTitle: 使用helmet.ieNoOpen()阻止IE打开不受信任的HTML
forumTopicId: 301584
localeTitle: 使用 helment.ieNoOpen() 防止 IE 打开不受信任的 HTML
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。某些Web应用程序将提供不受信任的HTML以供下载。默认情况下某些版本的Internet Explorer会在您的站点上下文中打开这些HTML文件。这意味着不受信任的HTML页面可能会在您的页面上下文中开始做坏事。此中间件将X-Download-Options标头设置为noopen。这将阻止IE用户在受信任站点的上下文中执行下载。 </section>
<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> 上克隆。
有些网站会下载不安全的 HTML 文件,某些版本的 IE 默认情况下还会在你网站的作用域下打开这些 HTML 文件。换句话说,这些不安全的 HTML 页面可以在你的网站做恶意行为。我们可以通过中间件来设置 header 中的 X-Download-Options 字段,让它的值为 noopen。这样就可以防止 IE 在不信任的网站下执行下载的文件。
</section>
## Instructions
<section id="instructions">
<section id='instructions'>
</section>
## Tests
@ -18,7 +22,7 @@ localeTitle: 使用helmet.ieNoOpen阻止IE打开不受信任的HTML
```yml
tests:
- text: helmet.ieNoOpen()中间件应正确安装
- 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); })
```
@ -34,7 +38,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,17 +2,21 @@
id: 587d8249367417b2b2512c3f
title: Set a Content Security Policy with helmet.contentSecurityPolicy()
challengeType: 2
videoUrl: ''
localeTitle: 使用helmet.contentSecurityPolicy设置内容安全策略
forumTopicId: 301585
localeTitle: 使用 helment.contentSecurityPolicy() 设置内容安全策略
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-infosec/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-infosec/">GitHub</a>克隆的。这一挑战突出了一个有希望的新防御它可以显着降低现代浏览器中许多类型攻击的风险和影响。通过设置和配置内容安全策略您可以防止在页面中无意中注入任何内容。这样可以保护您的应用免受XSS漏洞不受欢迎的跟踪恶意帧等攻击。 CSP通过定义受信任的内容源的白名单来工作。您可以为网页可能需要的每种资源脚本样式表字体框架媒体等等配置它们。有多个指令可用因此网站所有者可以拥有精细控制。有关详细信息请参阅HTML 5 RocksKeyCDN。不幸的是旧版浏览器不支持CSP。默认情况下指令是全开的因此将defaultSrc指令设置为回退非常重要。 Helmet支持defaultSrc和default-src命名样式。回退适用于大多数未指定的指令。在本练习中使用helmet.contentSecurityPolicy并将其配置为将defaultSrc指令设置为["self"]允许的源列表必须在数组中以便默认只信任您的网站地址。同时设置scriptSrc指令以便允许从您的网站和域"trusted-cdn.com"下载脚本。提示:在"&#39;self&#39;"关键字中,单引号是关键字本身的一部分,因此需要用双引号括起来才能生效。 </section>
<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> 上克隆。
在这个挑战中,我们要重点讨论现代浏览器中一种有效并且能大幅度减轻安全风险和很多种类型常见攻击的安全防护。通过配置内容安全策略,你可以防止很多类型的脚本恶意注入。这会让你的应用远离 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 id='instructions'>
这个练习中,我们使用 <code>helmet.contentSecurityPolicy()</code> 并配置 <code>defaultSrc</code><code>["self"]</code>(允许的资源列表必须在一个数组当中)。这样做表示只信任自己网站的域名。另外,配置 <code>scriptSrc</code> 指令可以限制脚本只能本网站域名上或者信任的域名 'trusted-cdn.com' 上下载
提示: 在 <code>self</code> 关键词中, 单引号是关键的一部分, 所以你应该用双引号来包起它才能正常工作
</section>
## Tests
@ -20,9 +24,9 @@ localeTitle: 使用helmet.contentSecurityPolicy设置内容安全策略
```yml
tests:
- text: helmet.csp()中间件应该正确安装。
- 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"]
- 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); })
```
@ -38,7 +42,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>

View File

@ -2,15 +2,21 @@
id: 58a25bcef9fc0f352b528e7c
title: Understand BCrypt Hashes
challengeType: 2
videoUrl: ''
localeTitle: 了解BCrypt Hashes
forumTopicId: 301586
localeTitle: 了解 BCrypt 的哈希加密
---
## Description
<section id="description">提醒一下,这个项目是基于<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-bcrypt/">Glitch</a>的以下入门项目构建的,或者是从<a href="https://github.com/freeCodeCamp/boilerplate-bcrypt/">GitHub</a>克隆的。 BCrypt哈希非常安全。哈希基本上是原始数据的指纹 - 始终是唯一的。这是通过将原始数据馈送到算法中并返回固定长度的结果来实现的。为了进一步使这个过程复杂化并使其更安全,您还可以<em>加入哈希</em>值。对哈希进行盐析涉及在哈希处理之前将随机数据添加到原始数据,这使得更难破解哈希。 BCrypt哈希总是看起来像<code>$2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>确实有一个结构。第一小部分数据<code>$2a</code>定义了使用何种哈希算法。下一部分<code>$13</code>定义了<em>成本</em> 。成本是计算哈希值所需的功率。它具有2 ^成本的对数标度并确定通过散列算法放置数据的次数。例如以10为代价您可以在普通计算机上每秒散列10个密码但是每个散列需要花费3秒才能进行散乱...并且需要花费更多时间成本为31需要多天才能完成哈希。目前12的成本被认为是非常安全的。哈希<code>$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>的最后一部分看起来像<code>$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm</code>数字句号和字母但它实际上是两条独立的信息。前22个字符是纯文本的盐其余的是哈希密码 <hr>要开始使用BCrypt请将其作为项目中的依赖项添加并在服务器中将其命名为“bcrypt”。当您认为自己已经做对时请提交您的页面。 </section>
<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>
## Instructions
<section id="instructions">
<section id='instructions'>
要开始使用 BCrypt, 只需将它添加到你的依赖列表,然后在你的服务器引入 'bcrypt'。
请在完成挑战后提交你的页面。
</section>
## Tests
@ -18,9 +24,9 @@ localeTitle: 了解BCrypt Hashes
```yml
tests:
- text: BCrypt是一个依赖
- 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
- 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); })
```
@ -36,7 +42,11 @@ tests:
<section id='solution'>
```js
// solution required
/**
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.
*/
```
/section>
</section>