freeCodeCamp/challenges/06-information-security-and.../information-security-with-h...

439 lines
19 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"name": "Information Security with HelmetJS",
"order": 1,
"time": "5 hours",
"helpRoom": "HelpBackend",
"challenges": [
{
"id": "587d8247367417b2b2512c36",
"title": "Install and Require Helmet.js",
"description": [
"Helmet helps you secure your Express apps by setting various HTTP headers. Install the package, then require it."
],
"challengeSeed": [],
"tests": [
{
"text": "\"helmet\" dependency should be in 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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8247367417b2b2512c37",
"title": "Hide potentially dangerous information using helmet.hidePoweredBy()",
"description": [
"Hackers can exploit known vulnerabilities in Express/Node if they see that your site is powered by Express. X-Powered-By: Express is sent in every request coming from Express by default. The helmet.hidePoweredBy() middleware will remove the X-Powered-By header. You can also explicitly set the header to something else, to throw people off. e.g. app.use(helmet.hidePoweredBy({ setTo: 'PHP 4.2.0' }))"
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.hidePoweredBy() middleware should be mounted correctly",
"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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8247367417b2b2512c38",
"title": "Mitigate the risk of clickjacking - helmet.frameguard()",
"description": [
"Your page could be put in a <frame> or <iframe> without your consent. This can result in clickjacking attacks, among other things. Clickjacking is a technique of tricking a user into interacting with a page different from what the user thinks it is. This can be obtained executing your page in a malicious context, by mean of iframing. In that context a hacker can put a hidden layer over your page. Hidden buttons can be used to run bad scripts. This middleware sets the X-Frame-Options header. It restricts who can put your site in a frame. It has three modes: DENY, SAMEORIGIN, and ALLOW-FROM.",
"We dont need our app to be framed. You should use helmet.frameguard() passing with the configuration object {action: 'deny'}."
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.frameguard() middleware should be mounted correctly",
"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' should be set to '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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8247367417b2b2512c39",
"title": "Mitigate the risk of Cross Site Scripting (XSS) Attacks - helmet.xssFilter()",
"description": [
"Cross-site scripting (XSS) is a frequent type of attack where malicious script are injected into vulnerable pages, on the purpouse of stealing sensitive data like session cookies, or passwords.",
"The basic rule to lower the risk of an XSS attack is simple: “Never trust users input”. As a developer you should always sanitize all the input coming from the outside. This includes data coming from forms, GET query urls, and even from POST bodies. Sanitizing means that you should find and encode the characters that may be dangerous e.g. <, >.",
"Modern browsers can help mitigating the risk by adopting better software strategies. Often these are configurable via http headers.",
"The X-XSS-Protection HTTP header is a basic protection. The browser detects a potential injected script using an heuristic filter. If the header is enabled, the browser changes the script code, neutralizing it.",
"It still has limited support."
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.xssFilter() middleware should be mounted correctly",
"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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8248367417b2b2512c3a",
"title": "Avoid inferring the response MIME type - helmet.noSniff()",
"description": [
"Browsers can use content or MIME sniffing to adapt to different datatypes coming from a response. They override the Content-Type headers to guess and process the data. While this can be convenient in some scenarios, it can also lead to some dangerous attacks. This middleware sets the X-Content-Type-Options header to nosniff. This instructs the browser to not bypass the provided Content-Type."
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.noSniff() middleware should be mounted correctly",
"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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8248367417b2b2512c3b",
"title": "Prevent IE from opening untrusted HTML - helmet.ieNoOpen()",
"description": [
"Some web applications will serve untrusted HTML for download. Some versions of Internet Explorer by default open those HTML files in the context of your site. This means that an untrusted HTML page could start doing bad things in the context of your pages. This middleware sets the X-Download-Options header to noopen. This will prevent IE users from executing downloads in the trusted sites context."
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.ieNoOpen() middleware should be mounted correctly",
"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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8248367417b2b2512c3c",
"title": "Ask browsers to access your site via HTTPS only - helmet.hsts()",
"description": [
"HTTP Strict Transport Security (HSTS) is a web security policy which helps to protect websites against protocol downgrade attacks and cookie hijacking. If your website can be accessed via HTTPS you can ask users browsers to avoid using insecure HTTP. By setting the header Strict-Transport-Security, you tell the browsers to use HTTPS for the future requests in a specified amount of time. This will work for the requests coming after the initial request.",
"Configure helmet.hsts() to use HTTPS for the next 90 days. Pass the config object {maxAge: timeInMilliseconds, force: true}. HyperDev already has hsts enabled. To override its settings you need to set the field \"force\" to true in the config object. We will intercept and restore the HyperDev header, after inspecting it for testing.",
"Note: Configuring HTTPS on a custom website requires the acquisition of a domain, and a SSL/TSL Certificate."
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.hsts() middleware should be mounted correctly",
"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 should be equal to 7776000 ms (90 days)",
"testString": "getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.match(data.headers['strict-transport-security'], /^max-age=777600000;?/); }, xhr => { throw new Error(xhr.responseText); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8248367417b2b2512c3d",
"title": "Disable DNS Prefetching - helmet.dnsPrefetchControl()",
"description": [
"To improve performance, most browsers prefetch DNS records for the links in a page. In that way the destination ip is already known when the user clicks on a link. This may lead to over-use of the DNS service (if you own a big website, visited by millions people…), privacy issues (one eavesdropper could infer that you are on a certain page), or page statistics alteration (some links may appear visited even if they are not). If you have high security needs you can disable DNS prefetching, at the cost of a performance penalty."
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.dnsPrefetchControl() middleware should be mounted correctly",
"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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8249367417b2b2512c3e",
"title": "Disable Client-Side Caching - helmet.noCache()",
"description": [
"If you are releasing an update for your website, and you want the users to always download the newer version, you can (try to) disable caching on clients browser. It can be useful in development too. Caching has performance benefits, and you will lose them, use this option only when there is a real need."
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.noCache() middleware should be mounted correctly",
"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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8249367417b2b2512c3f",
"title": "Set a Content Security Policy - helmet.contentSecurityPolicy()",
"description": [
"This challenge highlights one promising new defense that can significantly reduce the risk and impact of many type of attacks in modern browsers. By setting and configuring a Content Security Policy you can prevent the injection of anything unintended into your page. This will protect protect your app from XSS vulnerabilities, undesidered tracking, malicious frames, and much more. CSP works by defining a whitelist of content sources which are trusted. You can configure them for each kind of resource a web page may need (scripts, stylesheets, fonts, frames, media, and so on…). There are multiple directives available, so a website owner can have a granular control. See HTML 5 Rocks, KeyCDN for more details. Unfortunately CSP in unsupported by older browser.",
"By default, directives are wide open, so its important to set the defaultSrc directive as a fallback. Helmet supports both defaultSrc and default-src naming styles. The fallback applies for most of the unspecified directives. In this exercise, use helmet.contentSecurityPolicy(), and configure it setting the defaultSrc directive to [\"self\"] (the list of allowed sources must be in an array), in order to trust only your website address by default. Set also the scriptSrc directive so that you will allow scripts to be downloaded from your website, and from the domain 'trusted-cdn.com'.",
"Hint: in the \"'self'\" keyword, the single quotes are part of the keyword itself, so it needs to be enclosed in double quotes to be working."
],
"challengeSeed": [],
"tests": [
{
"text": "helmet.csp() middleware should be mounted correctly",
"testString": "getUserInput => $.get(getUserInput('url') + '/_api/app-info').then(data => { assert.include(data.appStack, 'csp'); }, xhr => { throw new Error(xhr.responseText); })"
},
{
"text": "Your csp config is not correct. defaultSrc should be [\"'self'\"] and scriptSrc should be [\"'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); })"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
},
{
"id": "587d8249367417b2b2512c40",
"title": "The parent helmet() middleware",
"description": [
"app.use(helmet()) will automatically include all the middleware introduced above, except noCache(), and contentSecurityPolicy(), but these can be enabled if necessary. You can also disable or configure any other middleware individually, using a configuration object.",
"// Example",
"<code>app.use(helmet({</code>",
"<code> frameguard: { // configure</code>",
"<code> action: 'deny'</code>",
"<code> },</code>",
"<code> contentSecurityPolicy: { // enable and configure</code>",
"<code> directives: {</code>",
"<code> defaultSrc: [\"self\"],</code>",
"<code> styleSrc: ['style.com'],</code>",
"<code> }</code>",
"<code> },</code>",
"<code> dnsPrefetchControl: false // disable</code>",
"<code>}))</code>",
"We introduced each middleware separately for teaching purpose, and for ease of testing. Using the parent helmet() middleware is easiest, and cleaner, for a real project."
],
"challengeSeed": [],
"tests": [
{
"text": "no tests - it's a descriptive challenge",
"testString": "assert(true)"
}
],
"solutions": [],
"hints": [],
"type": "backend",
"challengeType": 0,
"translations": {
"de": {
"description": [],
"title": ""
},
"fr": {
"description": [],
"title": ""
},
"pt-br": {
"description": [],
"title": ""
},
"ru": {
"description": [],
"title": ""
}
}
}
]
}