chore(learn): Applied MDX format to Chinese curriculum files (#40462)
This commit is contained in:
@@ -1,61 +1,87 @@
|
||||
---
|
||||
id: 587d824a367417b2b2512c45
|
||||
title: 匿名留言板
|
||||
challengeType: 4
|
||||
videoUrl: ''
|
||||
title: 匿名留言板
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">构建一个功能类似于此的完整堆栈JavaScript应用程序: <a href='https://spiky-well-vein.glitch.me/' target='_blank'>https://spiky-well-vein.glitch.me/</a> 。在这个项目上工作将涉及您在我们的入门项目上在Glitch上编写代码。完成此项目后,您可以将公共故障网址(到应用程序的主页)复制到此屏幕进行测试!您可以选择在另一个平台上编写项目,但必须公开显示我们的测试。使用<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-messageboard/">此链接</a>在Glitch上启动此项目或在GitHub上克隆<a href="https://github.com/freeCodeCamp/boilerplate-project-messageboard/">此存储库</a> !如果您使用Glitch,请记住将项目链接保存到安全的地方! </section>
|
||||
# --description--
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">
|
||||
</section>
|
||||
构建一个功能类似于此的完整堆栈JavaScript应用程序: <https://spiky-well-vein.glitch.me/> 。在这个项目上工作将涉及您在我们的入门项目上在Glitch上编写代码。完成此项目后,您可以将公共故障网址(到应用程序的主页)复制到此屏幕进行测试!您可以选择在另一个平台上编写项目,但必须公开显示我们的测试。使用[此链接](https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-messageboard/)在Glitch上启动此项目或在GitHub上克隆[此存储库](https://github.com/freeCodeCamp/boilerplate-project-messageboard/) !如果您使用Glitch,请记住将项目链接保存到安全的地方!
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
# --hints--
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 只允许您的网站加载到您自己网页上的iFrame中。
|
||||
testString: ''
|
||||
- text: 不允许DNS预取。
|
||||
testString: ''
|
||||
- text: 只允许您的网站为您自己的网页发送引荐来源。
|
||||
testString: ''
|
||||
- text: '我可以通过将表单数据文本和deletepassword_传递给/api/threads/ {board}将一个帖子发布到一个特定的留言板。(推荐res.redirect到电路板页面/ b / {board})保存的至少是_id,text ,createdon_(日期和时间),bumpedon_(日期和时间,与created_on相同),报告(布尔值),deletepassword_和回复(数组)。'
|
||||
testString: ''
|
||||
- text: '我可以通过将表单数据文本,deletepassword_和threadid_传递给/ api / replies / {board}来对特定板上的线程发送回复,并且还会将bumped_on日期更新到注释日期。(推荐res.redirect到thread page / b / {board} / {thread_id})在线程的回复数组中将保存_id,text,createdon_,deletepassword_,并报告。'
|
||||
testString: ''
|
||||
- text: '我可以在电路板上获取最近10个凸起线程的数组,其中最近只有来自/ api / threads / {board}的3个回复。 report和deletepasswords_字段不会发送到客户端。'
|
||||
testString: ''
|
||||
- text: '我可以使用/ api / replies / {board}的所有回复获取整个帖子吗?thread_id = {thread_id}。同样隐藏客户端应该看到的相同字段。'
|
||||
testString: ''
|
||||
- text: '如果我向/ api / threads / {board}发送DELETE请求并传递threadid_&deletepassword_,我可以完全删除一个线程。 (文字回复将是“密码不正确”或“成功”)'
|
||||
testString: ''
|
||||
- text: '如果我向/ api / replies / {board}发送DELETE请求并传递threadid_,replyid_和deletepassword_,我可以删除帖子(只是将文本更改为“[已删除]”而不是像线程一样完全删除)。 (文字回复将是“密码不正确”或“成功”)'
|
||||
testString: ''
|
||||
- text: '我可以通过向/ api / threads / {board}发送PUT请求并传递threadid_来报告一个线程并将其报告值更改为true。 (文字回复将是“成功”)'
|
||||
testString: ''
|
||||
- text: '我可以通过向/ api / replies / {board}发送PUT请求并传递threadid_&replyid_来报告回复并将其报告值更改为true。 (文字回复将是“成功”)'
|
||||
testString: ''
|
||||
- text: 完整的功能测试,完全测试路线和通过。
|
||||
testString: ''
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
只允许您的网站加载到您自己网页上的iFrame中。
|
||||
|
||||
```js
|
||||
// solution required
|
||||
|
||||
```
|
||||
|
||||
/section>
|
||||
不允许DNS预取。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
只允许您的网站为您自己的网页发送引荐来源。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
我可以通过将表单数据文本和deletepassword*传递给/api/threads/ {board}将一个帖子发布到一个特定的留言板。(推荐res.redirect到电路板页面/ b / {board})保存的至少是\_id,text ,createdon*(日期和时间),bumpedon*(日期和时间,与created_on相同),报告(布尔值),deletepassword*和回复(数组)。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
我可以通过将表单数据文本,deletepassword*和threadid*传递给/ api / replies / {board}来对特定板上的线程发送回复,并且还会将bumped*on日期更新到注释日期。(推荐res.redirect到thread page / b / {board} / {thread_id})在线程的回复数组中将保存\_id,text,createdon*,deletepassword\_,并报告。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
我可以在电路板上获取最近10个凸起线程的数组,其中最近只有来自/ api / threads / {board}的3个回复。 report和deletepasswords\_字段不会发送到客户端。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
我可以使用/ api / replies / {board}的所有回复获取整个帖子吗?thread_id = {thread_id}。同样隐藏客户端应该看到的相同字段。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
如果我向/ api / threads / {board}发送DELETE请求并传递threadid*&deletepassword*,我可以完全删除一个线程。 (文字回复将是“密码不正确”或“成功”)
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
如果我向/ api / replies / {board}发送DELETE请求并传递threadid*,replyid*和deletepassword\_,我可以删除帖子(只是将文本更改为“[已删除]”而不是像线程一样完全删除)。 (文字回复将是“密码不正确”或“成功”)
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
我可以通过向/ api / threads / {board}发送PUT请求并传递threadid\_来报告一个线程并将其报告值更改为true。 (文字回复将是“成功”)
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
我可以通过向/ api / replies / {board}发送PUT请求并传递threadid*&replyid*来报告回复并将其报告值更改为true。 (文字回复将是“成功”)
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
完整的功能测试,完全测试路线和通过。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@@ -3,15 +3,16 @@ id: 5e46f979ac417301a38fb932
|
||||
challengeType: 10
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
# --description--
|
||||
|
||||
Create a port scanner using Python.
|
||||
|
||||
You can access <a href='https://repl.it/@freeCodeCamp/fcc-port-scanner' target='_blank'>the full project description and starter code on repl.it</a>.
|
||||
You can access [the full project description and starter code on repl.it](https://repl.it/@freeCodeCamp/fcc-port-scanner).
|
||||
|
||||
After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
|
||||
|
||||
We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you some of the Python skills required for this project:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href='https://www.freecodecamp.org/news/python-for-everybody/'>Python for Everybody Video Course</a> (14 hours)
|
||||
@@ -21,37 +22,13 @@ We are still developing the interactive instructional part of the Python curricu
|
||||
</li>
|
||||
<ul>
|
||||
|
||||
</section>
|
||||
# --hints--
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
It should pass all Python tests.
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 'It should pass all Python tests.'
|
||||
testString: ''
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
```py
|
||||
# Python 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>
|
||||
|
@@ -3,82 +3,123 @@ id: 5e601c775ac9d0ecd8b94aff
|
||||
challengeType: 4
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
Develop a 2D real time multiplayer game using the HTML Canvas API and <a href='https://socket.io/' target='_blank'>Socket.io</a> that is functionally similar to this: <a href='https://thread-valley-lipstick.glitch.me/' target='_blank'>https://thread-valley-lipstick.glitch.me/</a>.
|
||||
# --description--
|
||||
|
||||
Develop a 2D real time multiplayer game using the HTML Canvas API and [Socket.io](https://socket.io/) that is functionally similar to this: <https://thread-valley-lipstick.glitch.me/>.
|
||||
|
||||
Working on this project will involve you writing your code on Glitch on our starter project. After completing this project you can copy your public glitch url (to the homepage of your app) into this screen to test it! Optionally you may choose to write your project on another platform but it must be publicly visible for our testing.
|
||||
|
||||
Start this project on Glitch using <a href='https://glitch.com/edit/#!/remix/clone-from-repo?REPO_URL=https://github.com/freeCodeCamp/boilerplate-project-secure-real-time-multiplayer-game/'>this link</a> or clone <a href='https://github.com/freeCodeCamp/boilerplate-project-secure-real-time-multiplayer-game'>this repository</a> on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!
|
||||
</section>
|
||||
Start this project on Glitch using [this link](https://glitch.com/edit/#!/remix/clone-from-repo?REPO_URL=https://github.com/freeCodeCamp/boilerplate-project-secure-real-time-multiplayer-game/) or clone [this repository](https://github.com/freeCodeCamp/boilerplate-project-secure-real-time-multiplayer-game) on GitHub! If you use Glitch, remember to save the link to your project somewhere safe!
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: Multiple players can connect to a server and play.
|
||||
testString: ''
|
||||
- text: Each player has an avatar.
|
||||
testString: ''
|
||||
- text: Each player is represented by an object created by the <code>Player</code> class in <code>Player.mjs</code>.
|
||||
testString: ''
|
||||
- text: At a minimum, each player object should contain a unique <code>id</code>, a <code>score</code>, and <code>x</code> and <code>y</code> coordinates representing the player's current position.
|
||||
testString: ''
|
||||
- text: The game has at least one type of collectible item. Complete the <code>Collectible</code> class in <code>Collectible.mjs</code> to implement this.
|
||||
testString: ''
|
||||
- text: At a minimum, each collectible item object created by the <code>Collectible</code> class should contain a unique <code>id</code>, a <code>value</code>, and <code>x</code> and <code>y</code> coordinates representing the item's current position.
|
||||
testString: ''
|
||||
- text: Players can use the WASD and/or arrow keys to move their avatar. Complete the <code>movePlayer</code> method in <code>Player.mjs</code> to implement this.
|
||||
testString: ''
|
||||
- text: |
|
||||
The <code>movePlayer</code> method should accept two arguments: a string of "up", "down", "left", or "right", and a number for the amount of pixels the player's position should change. <code>movePlayer</code> should adjust the <code>x</code> and <code>y</code> coordinates of the player object it's called from.
|
||||
testString: ''
|
||||
- text: The player's score should be used to calculate their rank among the other players. Complete the <code>calculateRank</code> method in the <code>Player</code> class to implement this.
|
||||
testString: ''
|
||||
- text: |
|
||||
The <code>calculateRank</code> method should accept an array of objects representing all connected players and return the string <code>Rank: currentRanking/totalPlayers</code>. For example, in a game with two players, if Player A has a score of 3 and Player B has a score of 5, <code>calculateRank</code> for Player A should return <code>Rank: 2/2</code>.
|
||||
testString: ''
|
||||
- text: Players can collide with a collectible item. Complete the <code>collision</code> method in <code>Player.mjs</code> to implement this.
|
||||
testString: ''
|
||||
- text: The <code>collision</code> method should accept a collectible item's object as an argument. If the player's avatar intersects with the item, the <code>collision</code> method should return <code>true</code>.
|
||||
testString: ''
|
||||
- text: All players are kept in sync.
|
||||
testString: ''
|
||||
- text: Players can disconnect from the game at any time.
|
||||
testString: ''
|
||||
- text: Prevent the client from trying to guess / sniff the MIME type.
|
||||
testString: ''
|
||||
- text: Prevent cross-site scripting (XSS) attacks.
|
||||
testString: ''
|
||||
- text: Nothing from the website is cached in the client.
|
||||
testString: ''
|
||||
- text: The headers say that the site is powered by "PHP 7.4.3" even though it isn't (as a security measure).
|
||||
testString: ''
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
Multiple players can connect to a server and play.
|
||||
|
||||
```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.
|
||||
*/
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
Each player has an avatar.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
Each player is represented by an object created by the `Player` class in `Player.mjs`.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
At a minimum, each player object should contain a unique `id`, a `score`, and `x` and `y` coordinates representing the player's current position.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
The game has at least one type of collectible item. Complete the `Collectible` class in `Collectible.mjs` to implement this.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
At a minimum, each collectible item object created by the `Collectible` class should contain a unique `id`, a `value`, and `x` and `y` coordinates representing the item's current position.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
Players can use the WASD and/or arrow keys to move their avatar. Complete the `movePlayer` method in `Player.mjs` to implement this.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
The `movePlayer` method should accept two arguments: a string of "up", "down", "left", or "right", and a number for the amount of pixels the player's position should change. `movePlayer` should adjust the `x` and `y` coordinates of the player object it's called from.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
The player's score should be used to calculate their rank among the other players. Complete the `calculateRank` method in the `Player` class to implement this.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
The `calculateRank` method should accept an array of objects representing all connected players and return the string `Rank: currentRanking/totalPlayers`. For example, in a game with two players, if Player A has a score of 3 and Player B has a score of 5, `calculateRank` for Player A should return `Rank: 2/2`.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
Players can collide with a collectible item. Complete the `collision` method in `Player.mjs` to implement this.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
The `collision` method should accept a collectible item's object as an argument. If the player's avatar intersects with the item, the `collision` method should return `true`.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
All players are kept in sync.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
Players can disconnect from the game at any time.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
Prevent the client from trying to guess / sniff the MIME type.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
Prevent cross-site scripting (XSS) attacks.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
Nothing from the website is cached in the client.
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
The headers say that the site is powered by "PHP 7.4.3" even though it isn't (as a security measure).
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@@ -3,15 +3,16 @@ id: 5e46f983ac417301a38fb933
|
||||
challengeType: 10
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
# --description--
|
||||
|
||||
For this project you will learn about the importance of good security by creating a password cracker to figure out passwords that were hashed using SHA-1.
|
||||
|
||||
You can access <a href='https://repl.it/@freeCodeCamp/fcc-brute-force-password-cracker' target='_blank'>the full project description and starter code on repl.it</a>.
|
||||
You can access [the full project description and starter code on repl.it](https://repl.it/@freeCodeCamp/fcc-brute-force-password-cracker).
|
||||
|
||||
After going to that link, fork the project. Once you complete the project based on the instructions in 'README.md', submit your project link below.
|
||||
|
||||
We are still developing the interactive instructional part of the Python curriculum. For now, here are some videos on the freeCodeCamp.org YouTube channel that will teach you some of the Python skills required for this project:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<a href='https://www.freecodecamp.org/news/python-for-everybody/'>Python for Everybody Video Course</a> (14 hours)
|
||||
@@ -20,37 +21,14 @@ We are still developing the interactive instructional part of the Python curricu
|
||||
<a href='https://www.freecodecamp.org/news/learn-python-basics-in-depth-video-course/'>Learn Python Video Course</a> (2 hours)
|
||||
</li>
|
||||
<ul>
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
<section id='instructions'>
|
||||
# --hints--
|
||||
|
||||
</section>
|
||||
It should pass all Python tests.
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 'It should pass all Python tests.'
|
||||
testString: ''
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
# --solutions--
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
```py
|
||||
# Python 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>
|
||||
|
@@ -1,51 +1,57 @@
|
||||
---
|
||||
id: 587d824a367417b2b2512c44
|
||||
title: 股票价格检查
|
||||
challengeType: 4
|
||||
videoUrl: ''
|
||||
title: 股票价格检查
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id="description">构建一个功能类似于此的完整堆栈JavaScript应用程序: <a href="https://sphenoid-crater.glitch.me/" target="_blank">https</a> : <a href="https://sphenoid-crater.glitch.me/" target="_blank">//giant-chronometer.glitch.me/</a> 。在这个项目上工作将涉及您在我们的入门项目上在Glitch上编写代码。完成此项目后,您可以将公共故障网址(到应用程序的主页)复制到此屏幕进行测试!您可以选择在另一个平台上编写项目,但必须公开显示我们的测试。使用<a href="https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-stockchecker/">此链接</a>在Glitch上启动此项目或在GitHub上克隆<a href="https://github.com/freeCodeCamp/boilerplate-project-stockchecker/">此存储库</a> !如果您使用Glitch,请记住将项目链接保存到安全的地方! </section>
|
||||
# --description--
|
||||
|
||||
## Instructions
|
||||
<section id="instructions">
|
||||
</section>
|
||||
构建一个功能类似于此的完整堆栈JavaScript应用程序: [https](https://sphenoid-crater.glitch.me/) : [//giant-chronometer.glitch.me/](https://sphenoid-crater.glitch.me/) 。在这个项目上工作将涉及您在我们的入门项目上在Glitch上编写代码。完成此项目后,您可以将公共故障网址(到应用程序的主页)复制到此屏幕进行测试!您可以选择在另一个平台上编写项目,但必须公开显示我们的测试。使用[此链接](https://glitch.com/#!/import/github/freeCodeCamp/boilerplate-project-stockchecker/)在Glitch上启动此项目或在GitHub上克隆[此存储库](https://github.com/freeCodeCamp/boilerplate-project-stockchecker/) !如果您使用Glitch,请记住将项目链接保存到安全的地方!
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
# --hints--
|
||||
|
||||
```yml
|
||||
tests:
|
||||
- text: 将内容安全策略设置为仅允许从服务器加载脚本和css。
|
||||
testString: ''
|
||||
- text: 我可以使用包含纳斯达克股票代码的表格数据获得/ api /股票价格并收回一个对象stockData。
|
||||
testString: ''
|
||||
- text: 在stockData中,我可以看到股票(字符串,股票代码),价格(字符串格式的小数)和喜欢(int)。
|
||||
testString: ''
|
||||
- text: 我也可以传递像true(boolean)这样的字段来将我的喜欢添加到股票中。每个IP应该只接受1个。
|
||||
testString: ''
|
||||
- text: 如果我传递2只股票,则返回对象将是一个包含股票信息的数组。而不是喜欢,它将显示两者上的rel_likes(两只股票之间的差异)。
|
||||
testString: ''
|
||||
- text: 获得当前价格的一个好方法是使用以下外部API(用您的股票替换“GOOG”):https://finance.google.com/finance/info?q = NASDAQ%3AOGOOG
|
||||
testString: ''
|
||||
- text: 所有5个功能测试都已完成并通过。
|
||||
testString: ''
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
<section id='challengeSeed'>
|
||||
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
将内容安全策略设置为仅允许从服务器加载脚本和css。
|
||||
|
||||
```js
|
||||
// solution required
|
||||
|
||||
```
|
||||
|
||||
/section>
|
||||
我可以使用包含纳斯达克股票代码的表格数据获得/ api /股票价格并收回一个对象stockData。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
在stockData中,我可以看到股票(字符串,股票代码),价格(字符串格式的小数)和喜欢(int)。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
我也可以传递像true(boolean)这样的字段来将我的喜欢添加到股票中。每个IP应该只接受1个。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
如果我传递2只股票,则返回对象将是一个包含股票信息的数组。而不是喜欢,它将显示两者上的rel_likes(两只股票之间的差异)。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
获得当前价格的一个好方法是使用以下外部API(用您的股票替换“GOOG”):https://finance.google.com/finance/info?q = NASDAQ%3AOGOOG
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
所有5个功能测试都已完成并通过。
|
||||
|
||||
```js
|
||||
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@@ -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--
|
||||
|
||||
|
@@ -4,27 +4,30 @@ challengeType: 11
|
||||
videoId: ugYfJNTawks
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
</section>
|
||||
# --question--
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
## --text--
|
||||
|
||||
```yml
|
||||
question:
|
||||
text: |
|
||||
Which socket object method lets you set the maximum amount of data your client accepts at once?
|
||||
Which socket object method lets you set the maximum amount of data your client accepts at once?
|
||||
|
||||
answers:
|
||||
- |
|
||||
`.recv(1024)`
|
||||
- |
|
||||
`.decode('ascii')`
|
||||
- |
|
||||
`.connect(host, port)`
|
||||
solution: 1
|
||||
```
|
||||
## --answers--
|
||||
|
||||
</section>
|
||||
`.recv(1024)`
|
||||
|
||||
---
|
||||
|
||||
`.decode('ascii')`
|
||||
|
||||
---
|
||||
|
||||
`.connect(host, port)`
|
||||
|
||||
## --video-solution--
|
||||
|
||||
1
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@@ -4,40 +4,43 @@ challengeType: 11
|
||||
videoId: CeGW761BIsA
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
</section>
|
||||
# --question--
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
## --text--
|
||||
|
||||
```yml
|
||||
question:
|
||||
text: |
|
||||
Fill in the blanks to complete the `banner` function below:
|
||||
Fill in the blanks to complete the `banner` function below:
|
||||
|
||||
```py
|
||||
def banner(ip, port):
|
||||
s = socket.socket()
|
||||
s.__A__((ip, __B__))
|
||||
print(s.recv(1024))
|
||||
```
|
||||
|
||||
answers:
|
||||
- |
|
||||
A: `connect`
|
||||
|
||||
B: `port`
|
||||
- |
|
||||
A: `getsockname`
|
||||
|
||||
B: `'1-1024'`
|
||||
- |
|
||||
A: `connect`
|
||||
|
||||
B: `int(port)`
|
||||
solution: 3
|
||||
```py
|
||||
def banner(ip, port):
|
||||
s = socket.socket()
|
||||
s.__A__((ip, __B__))
|
||||
print(s.recv(1024))
|
||||
```
|
||||
|
||||
</section>
|
||||
## --answers--
|
||||
|
||||
A: `connect`
|
||||
|
||||
B: `port`
|
||||
|
||||
---
|
||||
|
||||
A: `getsockname`
|
||||
|
||||
B: `'1-1024'`
|
||||
|
||||
---
|
||||
|
||||
A: `connect`
|
||||
|
||||
B: `int(port)`
|
||||
|
||||
## --video-solution--
|
||||
|
||||
3
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@@ -4,28 +4,30 @@ challengeType: 11
|
||||
videoId: z_qkqZS7KZ4
|
||||
---
|
||||
|
||||
## Description
|
||||
# --question--
|
||||
|
||||
<section id='description'>
|
||||
</section>
|
||||
## --text--
|
||||
|
||||
## Tests
|
||||
What is the main difference between the `.connect()` and `.connect_ex()` methods?
|
||||
|
||||
<section id='tests'>
|
||||
## --answers--
|
||||
|
||||
```yml
|
||||
question:
|
||||
text: |
|
||||
What is the main difference between the `.connect()` and `.connect_ex()` methods?
|
||||
There is no difference between the two methods.
|
||||
|
||||
answers:
|
||||
- |
|
||||
There is no difference between the two methods.
|
||||
- |
|
||||
If there is an error or if no host is found, `.connect()` returns an error code while `.connect_ex()` raises an exception.
|
||||
- |
|
||||
If there is an error or if no host is found, `.connect()` raises an exception while `.connect_ex()` returns an error code.
|
||||
solution: 3
|
||||
```
|
||||
---
|
||||
|
||||
If there is an error or if no host is found, `.connect()` returns an error code while `.connect_ex()` raises an exception.
|
||||
|
||||
---
|
||||
|
||||
If there is an error or if no host is found, `.connect()` raises an exception while `.connect_ex()` returns an error code.
|
||||
|
||||
## --video-solution--
|
||||
|
||||
3
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
# --solutions--
|
||||
|
||||
</section>
|
||||
|
@@ -4,27 +4,30 @@ challengeType: 11
|
||||
videoId: jYk9XaGoAnk
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
</section>
|
||||
# --question--
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
## --text--
|
||||
|
||||
```yml
|
||||
question:
|
||||
text: |
|
||||
What is the correct command to install the Python 3 version of the `python-nmap` library?
|
||||
What is the correct command to install the Python 3 version of the `python-nmap` library?
|
||||
|
||||
answers:
|
||||
- |
|
||||
`sudo apt install python-nmap`
|
||||
- |
|
||||
`pip install python-nmap`
|
||||
- |
|
||||
`pip3 install python-nmap`
|
||||
solution: 3
|
||||
```
|
||||
## --answers--
|
||||
|
||||
</section>
|
||||
`sudo apt install python-nmap`
|
||||
|
||||
---
|
||||
|
||||
`pip install python-nmap`
|
||||
|
||||
---
|
||||
|
||||
`pip3 install python-nmap`
|
||||
|
||||
## --video-solution--
|
||||
|
||||
3
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@@ -4,27 +4,30 @@ challengeType: 11
|
||||
videoId: a98PscnUsTg
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
</section>
|
||||
# --question--
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
## --text--
|
||||
|
||||
```yml
|
||||
question:
|
||||
text: |
|
||||
Which of the following allows you to scan for UDP ports between 21 to 443?
|
||||
Which of the following allows you to scan for UDP ports between 21 to 443?
|
||||
|
||||
answers:
|
||||
- |
|
||||
`.scan(ip_addr, '21-443', '-v -sU')`
|
||||
- |
|
||||
`.scan(ip_addr, '1-1024', '-v -sS')`
|
||||
- |
|
||||
`.scan(ip_addr, '21-443', '-v -sS')`
|
||||
solution: 1
|
||||
```
|
||||
## --answers--
|
||||
|
||||
</section>
|
||||
`.scan(ip_addr, '21-443', '-v -sU')`
|
||||
|
||||
---
|
||||
|
||||
`.scan(ip_addr, '1-1024', '-v -sS')`
|
||||
|
||||
---
|
||||
|
||||
`.scan(ip_addr, '21-443', '-v -sS')`
|
||||
|
||||
## --video-solution--
|
||||
|
||||
1
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
@@ -4,28 +4,30 @@ challengeType: 11
|
||||
videoId: XeQ7ZKtb998
|
||||
---
|
||||
|
||||
## Description
|
||||
# --question--
|
||||
|
||||
<section id='description'>
|
||||
</section>
|
||||
## --text--
|
||||
|
||||
## Tests
|
||||
What code editor and extension does the instructor recommend for developing penetration testing tools in Python?
|
||||
|
||||
<section id='tests'>
|
||||
## --answers--
|
||||
|
||||
```yml
|
||||
question:
|
||||
text: |
|
||||
What code editor and extension does the instructor recommend for developing penetration testing tools in Python?
|
||||
Atom and the atom-python-run extension.
|
||||
|
||||
answers:
|
||||
- |
|
||||
Atom and the atom-python-run extension.
|
||||
- |
|
||||
VSCode and Microsoft's Python extension.
|
||||
- |
|
||||
Sublime Text and the Anaconda package.
|
||||
solution: 2
|
||||
```
|
||||
---
|
||||
|
||||
VSCode and Microsoft's Python extension.
|
||||
|
||||
---
|
||||
|
||||
Sublime Text and the Anaconda package.
|
||||
|
||||
## --video-solution--
|
||||
|
||||
2
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
# --solutions--
|
||||
|
||||
</section>
|
||||
|
@@ -4,27 +4,30 @@ challengeType: 11
|
||||
videoId: F1QI9tNuDQg
|
||||
---
|
||||
|
||||
## Description
|
||||
<section id='description'>
|
||||
</section>
|
||||
# --question--
|
||||
|
||||
## Tests
|
||||
<section id='tests'>
|
||||
## --text--
|
||||
|
||||
```yml
|
||||
question:
|
||||
text: |
|
||||
Which of the following functions creates a socket object?
|
||||
Which of the following functions creates a socket object?
|
||||
|
||||
answers:
|
||||
- |
|
||||
`socket.bind((host, port))`
|
||||
- |
|
||||
`socket.gethostbyname()`
|
||||
- |
|
||||
`socket.socket(socket.AF_INET, socket.SOCK_STREAM)`
|
||||
solution: 3
|
||||
```
|
||||
## --answers--
|
||||
|
||||
</section>
|
||||
`socket.bind((host, port))`
|
||||
|
||||
---
|
||||
|
||||
`socket.gethostbyname()`
|
||||
|
||||
---
|
||||
|
||||
`socket.socket(socket.AF_INET, socket.SOCK_STREAM)`
|
||||
|
||||
## --video-solution--
|
||||
|
||||
3
|
||||
|
||||
# --hints--
|
||||
|
||||
|
||||
# --solutions--
|
||||
|
||||
|
Reference in New Issue
Block a user