{
    "componentChunkName": "component---src-templates-article-page-template-js",
    "path": "/ftw/how-to-sanitize-user-generated-content-in-ftw/",
    "result": {"data":{"markdownRemark":{"frontmatter":{"title":"How to sanitize user-generated content in FTW","slug":"how-to-sanitize-user-generated-content-in-ftw","updated":"2019-04-04T00:00:00.000Z","category":"ftw-security","ingress":"This guide describes how to sanitize user-generated content on Flex Template for Web (FTW).","skills":null},"htmlAst":{"type":"root","children":[{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"User-generated content (UGC) is a big source of Cross-Site Scripting\n(XSS) vulnerabilities so one should pay attention to it."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"By default,\n"},{"type":"element","tagName":"a","properties":{"href":"https://reactjs.org/docs/introducing-jsx.html#jsx-prevents-injection-attacks","target":"_blank","rel":["noopener","noreferrer"]},"children":[{"type":"text","value":"React DOM escapes any values embedded in JSX"}]},{"type":"text","value":"\nbefore rendering them. This makes customization a bit more\nstraightforward from XSS perspective."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"However, there are other XSS vulnerabilities to consider. User-generated\ncontent should be validated if passed to component props. For example,\n"},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"href"}]},{"type":"text","value":" attribute in "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<a>"}]},{"type":"text","value":" tag could potentially contain XSS attack\nvector since it allows JavaScript execution like\n"},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"href=\"javascript:alert('An XSS vulnerability found.')\""}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{"id":"first-things-first-set-up-content-security-policy","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#first-things-first-set-up-content-security-policy","ariaLabel":"first things first set up content security policy permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"First things first: set up Content Security Policy"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"One of the most advanced safety measures in modern browsers is Content\nSecurity Policy (CSP). When it is used, it helps to detect and mitigate\ncertain types of attacks, including XSS and data injection attacks. Read\nmore from our documentation on\n"},{"type":"element","tagName":"a","properties":{"href":"/docs/legacy/ftw/how-to-set-up-csp-for-ftw/"},"children":[{"type":"text","value":"how to set up Content Security Policy"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{"id":"sanitize-user-generated-content-when-updating-entities","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#sanitize-user-generated-content-when-updating-entities","ariaLabel":"sanitize user generated content when updating entities permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Sanitize user-generated content when updating entities"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"We have added an example of how user-generated content could be\nsanitized when entities are queried and updated to Redux store. Those\nexamples can be found from the utility file\n"},{"type":"element","tagName":"a","properties":{"href":"https://github.com/sharetribe/flex-template-web/blob/master/src/util/sanitize.js","target":"_blank","rel":["noopener","noreferrer"]},"children":[{"type":"text","value":"src/util/sanitize.js"}]},{"type":"text","value":"."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"You should modify those sanitize functions (e.g. "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"sanitizeUser"}]},{"type":"text","value":") to\ninclude any extendedData you have created for your marketplace. This is\nimportant if the data is used as a props/attribute in components (e.g.\n"},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<div attr=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...>content"}]},{"type":"text","value":"). As\nstated before, using it inside JSX components (e.g.\n"},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<div>{publicData.saunaRules}</div>"}]},{"type":"text","value":") is handled by React DOM."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"Those sanitize functions are used by default in "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"src/util/data.js"}]},{"type":"text","value":".\nThere is an "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"updatedEntities"}]},{"type":"text","value":" function, which is called before\nmarketplace entities are saved to Redux store."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"However, you should note that "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"updateEntities"}]},{"type":"text","value":" is called when something\nis added to the marketplaceData section of Redux store. So, it might not\ninclude all the places where entities are queried from Marketplace API.\nIn addition, you might also want to use sanitization directly in UI\ncomponents - for example, you could sanitize "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"href"}]},{"type":"text","value":" prop in\n"},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ExternalLink>"}]},{"type":"text","value":" component."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{"id":"other-things-to-consider","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#other-things-to-consider","ariaLabel":"other things to consider permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Other things to consider"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"It is a good practice to use wrapper components around elements that\nmight need extra safety measures. It makes it easier to add those extra\nmeasures if needed in the future. For example, instead of "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<a>"}]},{"type":"text","value":"\ncomponent, we recommend using "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<ExternalLink>"}]},{"type":"text","value":" component when you want\nto link outside of your web app. It adds a "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"noopener"}]},{"type":"text","value":" handling for\nexternal links (since "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"target=\"_blank\""}]},{"type":"text","value":" attribute is\n"},{"type":"element","tagName":"a","properties":{"href":"https://mathiasbynens.github.io/rel-noopener/","target":"_blank","rel":["noopener","noreferrer"]},"children":[{"type":"text","value":"vulnerable for XSS"}]},{"type":"text","value":")."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"p","properties":{},"children":[{"type":"text","value":"There is also Field* components around "},{"type":"element","tagName":"code","properties":{},"children":[{"type":"text","value":"<input>"}]},{"type":"text","value":" elements (e.g.\nFieldTextInput) since FTW uses Final Form. Those could be used in a\nsimilar fashion to validate content or just format it before saving."}]},{"type":"text","value":"\n"},{"type":"element","tagName":"h2","properties":{"id":"resources","style":"position:relative;"},"children":[{"type":"element","tagName":"a","properties":{"href":"#resources","ariaLabel":"resources permalink","className":["anchor","before"]},"children":[{"type":"element","tagName":"svg","properties":{"ariaHidden":"true","focusable":"false","height":"16","version":"1.1","viewBox":"0 0 16 16","width":"16"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","d":"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"},"children":[]}]}]},{"type":"text","value":"Resources"}]},{"type":"text","value":"\n"},{"type":"element","tagName":"ul","properties":{},"children":[{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"a","properties":{"href":"/docs/legacy/ftw/how-to-set-up-csp-for-ftw/"},"children":[{"type":"text","value":"How to set up Content Security Policy"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"a","properties":{"href":"https://reactjs.org/docs/introducing-jsx.html#jsx-prevents-injection-attacks","target":"_blank","rel":["noopener","noreferrer"]},"children":[{"type":"text","value":"JSX prevents injection attacks"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"a","properties":{"href":"https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.md","target":"_blank","rel":["noopener","noreferrer"]},"children":[{"type":"text","value":"OWASP XSS prevention cheat sheet"}]}]},{"type":"text","value":"\n"},{"type":"element","tagName":"li","properties":{},"children":[{"type":"element","tagName":"a","properties":{"href":"https://mathiasbynens.github.io/rel-noopener/","target":"_blank","rel":["noopener","noreferrer"]},"children":[{"type":"text","value":"https://mathiasbynens.github.io/rel-noopener/"}]}]},{"type":"text","value":"\n"}]}],"data":{"quirksMode":false}},"headings":[{"value":"First things first: set up Content Security Policy","depth":2},{"value":"Sanitize user-generated content when updating entities","depth":2},{"value":"Other things to consider","depth":2},{"value":"Resources","depth":2}]}},"pageContext":{"slug":"how-to-sanitize-user-generated-content-in-ftw","category":"ftw-security"}},
    "staticQueryHashes": ["3794076007","439097193","717698143"]}