Simply copy and paste the following command line in your terminal to create your first Strapi project.
npx create-strapi-app
my-project --quickstart
A content management system (CMS) enables users to create, manage, and publish content online without having to code. It simplifies the publishing process and provides some advanced features out of the box, like allowing multiple authors to work on different content at the same time.
CMSs have been around for a long time, and much of the web runs on them. A typical CMS has a backend where admins post content, tightly coupled with a frontend where content is rendered. These systems aren’t flexible, though, and leave you with few options when it comes to configurations.
On the other hand, a headless CMS does not have a frontend. It is a backend-only CMS that makes content available over an API. Content can be fetched and displayed using any frontend. This provides a high level of flexibility to developers, who can choose which user interface (UI), user experience (UX), stack, and frameworks to use. The increased usage of headless CMSs has re-revolutionized the way CMSs are used.
That includes Strapi, a headless CMS first released in 2015 and created using Node.js and React. It supports the databases PostgreSQL, SQLite, MySQL, and MariaDB; delivers content through a REST API; and supports the GraphQL query language.
Strapi has grown in popularity because most developers already use Node and React, making its code base easy to modify. As Strapi usage rises, though, so do the security challenges. There are certain security measures you can take to ensure a safer experience. This post will provide a Strapi security checklist.
A headless CMS uses a different approach to delivering content than a regular CMS. In a headless CMS, API calls are made to the backend, which either uses REST or GraphQL to deliver content. Allowing applications from a different domain to make requests to your app increases the risks of a security breach. Here are some of the hazards you could encounter in your Strapi application.
When using a REST API:
SQL injection: This technique allows a hacker to interfere with the request your application is making to the database. This is done by injecting malicious codes into your application. It works by passing specifically crafted inputs to the application (through comments or image uploads) that, when passed to the database unsanitized, can bypass security measures.
Denial-of-service (DoS) or distributed denial-of-service (DDoS) attack: The hacker sends dozens of requests to the server to exceed its bandwidth, destroying the connection of the host machine to the internet and making it inaccessible to users. This directly affects business performance and can lead to reputation decline.
Cross-site request forgery (CSRF): This attack uses an authenticated user’s connection to submit a malicious request to a web application in an attempt to steal data or cause damage.
When using a GraphQL API:
Brute-force: The attacker uses hacking tools to guess all possible combinations of passwords in order to access a database. This technique can also be used to attack a REST API.
Inconsistent authorization: When the resolver handles authorization directly in a GraphQL API, meaning authorization checks must be handled separately in each location, a forgotten check can expose the API to exploitation.
Failure to rate limit: Because a GraphQL API can be complex and consume a lot of resources, it may not implement rate-limiting effectively. This leaves it open to DoS attacks. In addition, its more complex queries can expose a vulnerability in the relationship between two object types.
Certain key areas of Strapi require special care in order to prevent attacks. Here are some best practices for keeping your app secure.
Strapi configurations require credentials for sensitive information about the app. If these details are hacked, your app is at risk. Always use a .env
file to securely store credentials within the application.
Credentials include information like API keys, database usernames, and passwords. In addition to using .env
files, be sure not to do this:
Especially if your app is in production, this exposes sensitive information.
Create a .env
file at the root of your application and populate it with credentials. Below is an example of how data can be stored in a .env
file.
1
DATABASE_PASSWORD=myPassword
You can easily get access to this data in the config file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = ({ env }) => ({
connections: {
default: {
settings: {
password: env('DATABASE_PASSWORD'),
},
},
},
});
Check the official documentation for more information.
Validation verifies the authenticity of user input. Every input should be properly validated to ensure the correct data type and prevent the input of malicious codes. Strapi object-relational mapping (ORM) has an inbuilt validation implementation. But the wrong data type can cause a crash, and the error reporting can be faulty.
You can validate your inputs using joi, a JavaScript validation package. Its simple syntax allows you to validate your data in a readable way.
Install joi using npm:
1
npm install joi
The documentation offers rich tutorials.
Sanitization removes any malicious code coming from the user’s input. Strapi uses ORMs like Bookshelf, which sanitizes data before storing. To be even safer, you can apply another layer of sanitization to your code before data reaches the Strapi ORM, especially if your input involves users entering HTML or coding languages.
sanitize-html is well suited for cleaning up HTML fragments and for removing unwanted CSS when copying and pasting from Word.
Roles and permissions determine which user has access to what route in the Strapi application. Strapi by default places restrictions on certain routes, but developers sometimes grant permissions to all routes:
A hacker can easily access these routes using a tool like Postman, Vanilla JavaScript, or any frontend framework of choice. Make sure that the role and permission of every route in your app is properly set up. You can go the extra mile by adding policies to each route.
Policies can be found in Strapi routes like so:
They are functions that run and execute certain logics before the request gets to the controller. Policies are created mainly to set restrictions on certain features in the application. For example, a policy can ensure a user is logged in before allowing them to write a post.
To create a policy, create a /policies
folder in ./api/**/config
of your project directory or plugins directory. You can also create a global policy in ./config/policies/
at the root of your application.
For example, say you create a global policy that ensures a user is logged in before carrying out any action. In /config/policies
create the JavaScript file name auth-check.js
and add the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = async (ctx, next) => {
if (ctx.state.user) {
return await next();
}
ctx.unauthorized(`You're not logged in!`);
};
This checks to see if the user has an active session. If it does not return true, the user will be notified that they are not logged in.
You can call the policy in the route like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"routes": [
{
"method": "GET",
"path": "/restaurants",
"handler": "Restaurant.find",
"config": {
"policies": ["global::auth-check"]
}
}
]
}
Policies are useful in Strapi and deserve to be studied in depth. For more details, check the documentation.
Data leakage is one of the most important factors in a security check. Sensitive user data stored in your application should not be released to an untrusted source, either intentionally or unintentionally.
In Strapi, a collection type can have a one-to-many or a many-to-many relationship with the user. Making a GET request to the collection type will return the user data as part of the object. This is a form of data leakage.
For example, say you have an article collection type belonging to a user. The user object contains sensitive details like username, address, and phone number. If you fetch the article, you will get the user details along with it:
To fix this, pass private: true
in the model of your collection type.
This restricts access to the user data but won’t break your code. If you make the request again, you will get your article data without the user data.
The security of your application is just as important as every other feature in the application. You don’t want your business or your users to fall victim to attackers.
There are multiple possible security vulnerabilities in a Strapi app, but fortunately there are practices you can use to prevent them. If you take the time to secure your app, you will ensure greater success for you and your business.
Anumadu is a fullstack engineer, but also a technical writer specialized in Laravel, Vue.js, Nuxt.js and Node.js.