Web security
You can protect your web applications from common web attacks like CSRF, XSS, content sniffing and more using the @adonisjs/shield
package.
It is recommended to use this package when creating a server-rendered app using AdonisJS.
If you are using AdonisJS to create an API server, then you must rely on your frontend framework's security layer.
npm i @adonisjs/shield@7.1.1
node ace configure @adonisjs/shield
// Add following to start/kernel.ts
Server.middleware.register([
() => import('@ioc:Adonis/Core/BodyParser'),
() => import('@ioc:Adonis/Addons/Shield')
])
CSRF protection
CSRF (Cross-Site Request Forgery) is an attack that tricks the user of your web apps to perform form submissions without their explicit consent.
To protect against the CSRF attacks, your application should be able to distinguish between the form submissions triggered by your app vs. some other malicious website.
AdonisJS generates a unique token (known as CSRF token) for every HTTP request and associates it with the user session for later verification. Since, the token is generated on the backend, the malicious website has no way of getting access to it.
The token must be present alongside the other form fields in order for CSRF check to pass. You can access it using the csrfField
inside your Edge templates.
<form action="{{ route('PostsController.store') }}" method="post">
{{ csrfField() }}
<div>
<label for="title">Post title</label>
<input type="text" name="title">
</div>
<hr>
<button type="submit">Create Post</button>
</form>
That is all you need to do.
Configuration
The shield middleware relies on the config stored inside the config/shield.ts
file. Feel free to tweak the configuration options as per your requirements.
export const csrf: ShieldConfig['csrf'] = {
enabled: true,
exceptRoutes: [],
enableXsrfCookie: true,
methods: ['POST', 'PUT', 'PATCH', 'DELETE'],
cookieOptions: {
domain: '',
path: '/',
maxAge: '2h',
httpOnly: true,
secure: false,
sameSite: false,
}
}
enabled
Enable/disable the CSRF protection all together. You may find yourself disabling it during tests when hitting the form endpoints directly.
exceptRoutes
Ignore certain routes from being validated for the CSRF token. You may find it useful, when creating a hybrid app with API endpoints and the server rendered forms by exempting API endpoints from CSRF token validation.
{
exceptRoutes: [
'/api/users',
'/api/users/:id',
'/api/posts'
]
}
For more advanced use cases, you can register a function and dynamically filter routes from being validated.
{
exceptRoutes: (ctx) => {
// ignore all routes starting with /api/
return ctx.request.url().includes('/api/')
}
}
methods
HTTP methods to validate for the availability of the CSRF token. You must add all the HTTP verbs you are using to handle form submissions.
{
methods: ['POST', 'PUT', 'PATCH', 'DELETE']
}
enableXsrfCookie
Setting the value to true
instructs the shield middleware to read the CSRF token from the X-XSRF-TOKEN
header. Read the Ajax form submissions
section to learn more.
cookieOptions
An object of cookie options. Read the Cookie section to learn more.
CSRF token for SPA
The Single page applications render forms on the frontend and hence they do not have access to the csrfField
view global. However, you can read the token value from the XSRF-TOKEN
cookie and send it to the server via X-XSRF-TOKEN
header.
The cookie technique is already widely supported by frameworks like Angular and request libraries like axios.
However, do make sure to enable the cookie feature by setting the value of enableXsrfCookie = true
inside the config/shield.ts
file.
CSRF token for RESTful APIs
If you are creating RESTful API server, then you don't need CSRF protection, unless you are relying on cookies for user authentication. If you are relying on cookies for authentication, then simply follow the instructions of CSRF token for SPA section.
CSP
CSP (Content security policy) helps you define the trusted sources for loading and executing scripts, styles, fonts, etc and reduce the risk of XSS attacks.
You can configure the CSP header by tweaking the configuration options inside the config/shield.ts
file.
export const csp: ShieldConfig['csp'] = {
enabled: false,
directives: {},
reportOnly: false,
}
enabled
Enable/disable CSP protection all together.
directives
Configure the CSP header directives. We recommend reading about them on https://content-security-policy.com
. The dash-case
directive names are defined as camelCase
inside the shield config file.
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'https://cdnjs.cloudflare.com', '@nonce'],
fontSrc: ["'self'", 'https://fonts.googleapis.com'],
}
reportOnly
Set the value to true, if you want the CSP violations to result in a warning rather than an error. Learn more .
CSP nonce
To define nonce-based
inline script and style tags, you have to make use of the @nonce
keyword.
directives: {
scriptSrc: ["'self'", '@nonce'],
}
Next, make use of the cspNonce
view helper to define the nonce attribute on the inline script and style tags.
<script nonce="{{ cspNonce }}">
</script>
You can also access the nonce
attribute using the response.nonce
property.
Route.get('/', ({ response }) => {
return {
nonce: response.nonce
}
})
DNS prefetching
Using the dnsPrefetch
setting from the config/shield.ts
file, you can control the behavior for the X-DNS-Prefetch-Control
header.
export const dnsPrefetch: ShieldConfig['dnsPrefetch'] = {
enabled: true,
allow: true,
}
enabled
Enable/disable the header all together.
allow
Setting the value to true will define the X-DNS-Prefetch-Control
header with the value 'on'
, otherwise 'off'
value is defined.
Frame guard
The xFrame
config property manages the X-Frame-Options
header.
export const xFrame: ShieldConfig['xFrame'] = {
enabled: true,
action: 'DENY',
}
enabled
Enable/disable the header all together.
action
Define the header value. It must be one from DENY
, SAMEORIGIN
or ALLOW-FROM
. The ALLOW-FROM
action also needs the domain name to allow.
{
enabled: true,
action: 'ALLOW-FROM',
domain: 'foo.com'
}
HSTS
Control whether or not the website should be accessible via HTTP using the Strict-Transport-Security header.
The configuration for HSTS is stored inside the config/shield.ts
file.
export const hsts: ShieldConfig['hsts'] = {
enabled: true,
maxAge: '180 days',
includeSubDomains: true,
preload: false,
}
enabled
Enable/disable the Strict-Transport-Security
all together.
maxAge
Define how long the browser should remember the header value.
includeSubDomains
When set to true
, the rule will be applied to site's subdomains as well.
preload
Whether or not to preload the header value from the HSTS preload service. Learn more
No sniffing
Using the contentTypeSniffing
setting you can control the behavior for the X-Content-Type-Options
header.
The header is only set when the enabled
property is set to true.
export const contentTypeSniffing: ShieldConfig['contentTypeSniffing'] = {
enabled: true,
}