Skip to content

Commit 8410839

Browse files
amirhhashemikodiakhq[bot]LadyBluenotes
authored
Improve security docs (#1103)
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Sarah <gerrardsarah@gmail.com>
1 parent 586a866 commit 8410839

File tree

1 file changed

+191
-21
lines changed

1 file changed

+191
-21
lines changed

src/routes/solid-start/guides/security.mdx

Lines changed: 191 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,205 @@
22
title: Security
33
---
44

5-
As a non-opinionated framework, SolidStart doesn't enforce any security practices, though it enables developers to implement them as needed.
6-
It is important to know what are the requirements for your own app and implement the fitting security measures.
7-
If at any point you are unsure about the security of your app, or how to achieve something within the constraints of SolidStart reach us on [Discord](https://discord.gg/solidjs).
5+
## XSS (Cross Site Scripting)
86

9-
Below you will find a few notes on how to establish some measures.
7+
Solid automatically escape values passed to JSX expressions to reduce the risk of XSS attacks.
8+
However, this protection does not apply when using [`innerHTML`](/reference/jsx-attributes/innerhtml-or-textcontent#innerhtml-or-textcontent).
109

11-
## Security Headers
10+
To protect your application from XSS attacks:
1211

13-
Through the use of a [middleware](/solid-start/reference/server/create-middleware#example) it is possible to tab into the `onRequest` event handlers and make sure every request going through your servers have the proper security headers set.
14-
With this, it is possible to setup headers like `Content-Security-Policy`, `X-Frame-Options`, `X-XSS-Protection`, `X-Content-Type-Options`, among others.
12+
- Avoid using `innerHTML` when possible.
13+
If necessary, make sure to sanitize user-supplied data with libraries such as [DOMPurify](https://github.com/cure53/DOMPurify).
14+
- Validate and sanitize user inputs, especially form inputs on the server and client.
15+
- Set a [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP).
16+
- Sanitize attributes containing user-supplied data within `<noscript>` elements.
17+
This includes both the attributes of the `<noscript>` element itself and its children.
1518

16-
### Nonces
19+
It is highly recommended to read the [Cross Site Scripting Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) for further guidance.
1720

18-
When using `Content-Security-Policy` it is possible to use nonces to allow inline scripts and styles to be executed.
19-
SolidStart enables that smoothly in the [`entry-server.tsx`](/solid-start/reference/entrypoints/entry-server).
21+
## Content Security Policy (CSP)
2022

21-
By passing generating the `nonce` within a middleware and storing it in the `request.locals` object, it is possible to use it in the `entry-server.tsx` to generate the `Content-Security-Policy` header.
23+
To configure the `Content-Security-Policy` HTTP header, a [middleware](/solid-start/advanced/middleware) can be used.
2224

23-
## Cross Request Forgery (CSRF)
25+
### With nonce (recommended)
2426

25-
There are multiple ways to add CSRF Protection to your SolidStart app.
26-
The quickest and most common way is to check the `request.referrer` header when the HTTP method is `POST`, `PUT`, `PATCH` or `DELETE`.
27-
This can also be achieved through an `onRequest` [middleware](/solid-start/reference/server/create-middleware#example).
27+
If you want to use a [strict CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP#strict_csp) with nonces:
2828

29-
## Cross Site Scripting (XSS)
29+
1. Create a middleware that configures the CSP header.
30+
It must then be registered using the [`onRequest`](/solid-start/advanced/middleware#onrequest) event.
31+
2. Create a nonce using a cryptographic random value generator, such as the [`randomBytes`](https://nodejs.org/api/crypto.html#cryptorandombytessize-callback) function from the `crypto` module.
32+
3. Store the nonce in the [`locals`](/solid-start/advanced/middleware#locals) object.
33+
4. Configure SolidStart to use the nonce in your [`entry-server.tsx`](/solid-start/reference/entrypoints/entry-server) file.
3034

31-
SolidStart automatically escape inserts and attributes in HTML.
32-
The exception is when HTML is inserted via the `innerHTML` property, which bypasses the escaping.
33-
Additionally, it's important to note that `<noscript>` are also outside of the purview of SolidStart, since those tags and its contents are evaluated even without JavaScript.
34-
It is important to sanitize any strings in attributes, especially when inside `<noscript>` tags.
35+
<TabsCodeBlocks>
36+
<div id="Middleware">
3537

36-
As a rule-of-thumb it is recommended to avoid injecting HTML into your page as much as possible, make sure the contents of `<noscript>` are properly sanitized, and add a strict Content Security Policy to your application.
38+
```tsx
39+
import { createMiddleware } from "@solidjs/start/middleware";
40+
import { randomBytes } from "crypto";
41+
42+
export default createMiddleware({
43+
onRequest: (event) => {
44+
const nonce = randomBytes(16).toString("base64");
45+
46+
event.locals.nonce = nonce;
47+
48+
const csp = `
49+
default-src 'self';
50+
script-src 'nonce-${nonce}' 'strict-dynamic' 'unsafe-eval';
51+
object-src 'none';
52+
base-uri 'none';
53+
frame-ancestors 'none';
54+
form-action 'self';
55+
`.replace(/\s+/g, " ");
56+
57+
event.response.headers.set("Content-Security-Policy", csp);
58+
},
59+
});
60+
```
61+
62+
</div>
63+
<div id="entry-server.tsx">
64+
65+
```tsx {6} title="src/entry-server.tsx"
66+
// @refresh reload
67+
import { createHandler, StartServer } from "@solidjs/start/server";
68+
69+
export default createHandler(
70+
() => <StartServer /* ... */ />,
71+
(event) => ({ nonce: event.locals.nonce })
72+
);
73+
```
74+
75+
</div>
76+
</TabsCodeBlocks>
77+
78+
### Without nonce
79+
80+
To configure CSP without a nonce, a middleware that sets the CSP header is required, and it should be registered to run during the [`onBeforeResponse`](/solid-start/advanced/middleware#onbeforeresponse) event:
81+
82+
```tsx
83+
import { createMiddleware } from "@solidjs/start/middleware";
84+
85+
export default createMiddleware({
86+
onBeforeResponse: (event) => {
87+
const csp = `
88+
default-src 'self';
89+
font-src 'self' ;
90+
object-src 'none';
91+
base-uri 'none';
92+
frame-ancestors 'none';
93+
form-action 'self';
94+
`.replace(/\s+/g, " ");
95+
96+
event.response.headers.set("Content-Security-Policy", csp);
97+
},
98+
});
99+
```
100+
101+
## CORS (Cross-Origin Resource Sharing)
102+
103+
When other applications need access to API endpoints, a middleware that configures the CORS headers is needed:
104+
105+
```tsx
106+
import { createMiddleware } from "@solidjs/start/middleware";
107+
import { json } from "@solidjs/router";
108+
109+
const TRUSTED_ORIGINS = ["https://my-app.com", "https://another-app.com"];
110+
111+
export default createMiddleware({
112+
onBeforeResponse: (event) => {
113+
const { request, response } = event;
114+
115+
response.headers.append("Vary", "Origin, Access-Control-Request-Method");
116+
117+
const origin = request.headers.get("Origin");
118+
const requestUrl = new URL(request.url);
119+
const isApiRequest = requestUrl && requestUrl.pathname.startsWith("/api");
120+
121+
if (isApiRequest && origin && TRUSTED_ORIGINS.includes(origin)) {
122+
// Handle preflight requests.
123+
if (
124+
request.method === "OPTIONS" &&
125+
request.headers.get("Access-Control-Request-Method")
126+
) {
127+
// Preflight requests are standalone, so we immediately send a response.
128+
return json(null, {
129+
headers: {
130+
"Access-Control-Allow-Origin": origin,
131+
"Access-Control-Allow-Methods": "OPTIONS, POST, PUT, PATCH, DELETE",
132+
"Access-Control-Allow-Headers": "Authorization, Content-Type",
133+
},
134+
});
135+
}
136+
137+
// Handle normal requests.
138+
response.headers.set("Access-Control-Allow-Origin", origin);
139+
}
140+
},
141+
});
142+
```
143+
144+
## CSRF (Cross-Site Request Forgery)
145+
146+
To prevent CSRF attacks, a middleware can be used to block untrusted requests:
147+
148+
```tsx
149+
import { createMiddleware } from "@solidjs/start/middleware";
150+
import { json } from "@solidjs/router";
151+
152+
const SAFE_METHODS = ["GET", "HEAD", "OPTIONS", "TRACE"];
153+
const TRUSTED_ORIGINS = ["https://another-app.com"];
154+
155+
export default createMiddleware({
156+
onRequest: (event) => {
157+
const { request } = event;
158+
159+
if (!SAFE_METHODS.includes(request.method)) {
160+
const requestUrl = new URL(request.url);
161+
const origin = request.headers.get("Origin");
162+
163+
// If we have an Origin header, check it against our allowlist.
164+
if (origin) {
165+
const parsedOrigin = new URL(origin);
166+
167+
if (
168+
parsedOrigin.origin !== requestUrl.origin &&
169+
!TRUSTED_ORIGINS.includes(parsedOrigin.host)
170+
) {
171+
return json({ error: "origin invalid" }, { status: 403 });
172+
}
173+
}
174+
175+
// If we are serving via TLS and have no Origin header, prevent against
176+
// CSRF via HTTP man-in-the-middle attacks by enforcing strict Referer
177+
// origin checks.
178+
if (!origin && requestUrl.protocol === "https:") {
179+
const referer = request.headers.get("Referer");
180+
181+
if (!referer) {
182+
return json({ error: "referer not supplied" }, { status: 403 });
183+
}
184+
185+
const parsedReferer = new URL(referer);
186+
187+
if (parsedReferer.protocol !== "https:") {
188+
return json({ error: "referer invalid" }, { status: 403 });
189+
}
190+
191+
if (
192+
parsedReferer.host !== requestUrl.host &&
193+
!TRUSTED_ORIGINS.includes(parsedReferer.host)
194+
) {
195+
return json({ error: "referer invalid" }, { status: 403 });
196+
}
197+
}
198+
}
199+
},
200+
});
201+
```
202+
203+
This example demonstrates a basic CSRF protection that verifies the `Origin` and `Referer` headers, blocking requests from untrusted origins.
204+
Additionally, consider implementing a more robust CSRF protection mechanism, such as the [Double-Submit Cookie Pattern](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#alternative-using-a-double-submit-cookie-pattern).
205+
206+
For further guidance, you can look at the [Cross-Site Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html).

0 commit comments

Comments
 (0)