”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 前端和后端开发的关键安全原则

前端和后端开发的关键安全原则

发布于2024-11-03
浏览:521

Key Security principles for Frontend and backend Development

Introduction:

Building a full-stack application is a rewarding challenge, but it's crucial to prioritize security from the very start. Have you considered the essential security principles needed to protect your product? How can we ensure that the application meets security standards effectively, and how easy is it to implement these principles?

In this article, I will explore fundamental security principles for both frontend and backend development. We’ll also discuss practical steps to implement them and ensure that your app adheres to industry security standards.

  1. Securing the frontend:
  • Input Validation and Sanitization:

-Ensure that all input from users is properly validated and sanitized to avoid injection attacks such as Cross-Site Scripting (XSS).

-Use libraries like DOMPurify for sanitizing user inputs that will be inserted into the DOM.

  • Authentication and Authorization:

-Implement secure authentication methods such as OAuth or the famous JWT method (JSON Web Tokens) to ensure that users are who they claim to be.
-Use role-based access control (RBAC) to restrict parts of the UI based on user permissions, for example part of the UI reserved to admin and other part for ordinary users.

// the code below show an example of how user 
//should be authenticated and depending 
//on his role the dashboard UI display
const createRoutes = (logged: boolean, role:string) =>
  createBrowserRouter([
{
          path: "/dashboard",
          loader: combinedDashboardLoader,

          element: (
            
              {role==="admin"}?:
          ),
          errorElement: ,
        }])

  • Secure Storage of sensitive Data:

-Avoid storing sensitive information like access tokens in localStorage or sessionStorage as they are vulnerable to XSS. Instead, use secure cookies with the HttpOnly and Secure flags enabled.

// in your server code you can write this code
//  inside your login controller function
// so you don't need to send your token to the frontend
res.cookie("accessToken", accessToken, {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
      maxAge: expirationTime * 1000,
      sameSite: "lax",
    });

  • CORS (Cross-Origin Resource Sharing):

-Implement proper CORS policies to only allow specific origins to interact with your backend API. Ensure that you restrict methods and headers that are allowed.

  • Content Security Policy (CSP):

-Set up a robust CSP to prevent the execution of untrusted scripts on your frontend. This reduces the risk of XSS attacks by controlling where resources can be loaded from.
-_Implementation _:
the easiest way is to simply use meta tags in your head of the html page as shown below in the code , for more details you can refer to this link https://www.stackhawk.com/blog/react-content-security-policy-guide-what-it-is-and-how-to-enable-it/

  
    ...
    

-Alternatively you can a middleware in your EpressJS as below:

app.use((req, res, next) => {
  res.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' https://trusted.cdn.com; object-src 'none';");
  next();
});

  • HTTPS:

Enforce HTTPS to encrypt communication between the client and the server, ensuring that data is not exposed during transit.

  1. Securing the backend:
  • Input Validation and Sanitization (Server-side):

-Always validate and sanitize user inputs on the server side to prevent SQL injection or other code injection attacks. Even if input validation is done on the frontend, it should always be performed on the backend as well.
below an example using express validator middleware, then simply apply this middleware before your registration controller function.

import { body, validationResult } from "express-validator";
const validateUserInput = [
    body("email").isEmail().withMessage("Please enter a valid email address"),
    body("name")
        .isLength({ min: 3 })
        .withMessage("Name must be at least 3 characters long"),
    body("password")
        .isLength({ min: 6 })
        .withMessage("Password must be longer than 5 characters"),

    // Middleware to check for validation errors
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array()[0].msg });
        }
        next();
    },
];
export default validateUserInput;
//in your router file apply it 
//register is the post request function to add new user
router.route("/register").post(validateUserInput, register);

  • Authentication and Authorization:

-Use robust authentication mechanisms, such as OAuth, and secure tokens like JWT for session management.

-Implement role-based access control (RBAC) and avoid hardcoding permissions directly in your application.

  • Password security:

-Store passwords securely using strong hashing algorithms like bcrypt or Argon2. Never store plain text passwords.

-If needed implement multi-factor authentication (MFA) for an additional layer of security.

the code below shows how the process of securing password and using JWT (authentication).

// hashing password inside your registration function  
const register = async (req: Request, res: Response) => {
  const { email, password, name } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
createUser(email,name,hashedpassword) //a function to add new user into your DB
}

// Compare the password to the hashed one during the login request
const login = async (req: Request, res: Response) => {
  const { email, password } = req.body;
const existingUser = await query(findUserByEmail, [email]);
    if (!existingUser[0])
      return res.status(404).json({
        message: `${email} not found! register if you don't have an account`,
      });
    const matchPassword = await bcrypt.compare(
      password,
      existingUser[0].password
    );
//generate token JWT
    const expirationTime = 180;
    const accessToken = jwt.sign(
      { id: existingUser[0].iduser },
      process.env.ACCESS_TOKEN_SECRET!,
      {
        expiresIn: expirationTime   "s",
      }
    );}

  • Rate Limiting and Throttling: To prevent brute-force attacks, implement rate limiting and IP-based throttling on sensitive endpoints such as login routes.

Rate Limiting:
It restricts the number of requests a user (identified by IP, API key, etc.) can make to the server in a given time window (e.g., 100 requests per minute). After the limit is reached, further requests are blocked or delayed.

const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();

// Define rate limiting rule: 100 requests per 15 minutes
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests 
  message: 'Too many requests from this IP, please try again later.', // Custom message 
  standardHeaders: true,  `RateLimit-*` headers
  legacyHeaders: false, 
});

// Apply the rate limiting middleware to all requests

app.use(limiter);

//but we can also apply it to a specific route 
app.post('/login', limiter, (req, res) => {
  res.send('Login route');
});


app.listen(3000, () => {
  console.log('Server is running on port 3000');
});


Throttling:
Similar to rate limiting, but instead of blocking requests entirely, it slows down the processing of requests after a certain threshold is reached. This can avoid service overloads.

Example:
If a user makes more than 10 API requests per second, you slow down the response to every subsequent request to ensure the system isn’t overwhelmed.

  • Data encryption:

Use encryption for sensitive data both at rest and in transit. Ensure that databases and other storage mechanisms encrypt sensitive information.

the code below shows an implementation of data encryption of a credit card number before storing it in the database.

const crypto = require('crypto');

// Encryption settings
const encryptionKey = crypto.randomBytes(32); // AES-256 key
const iv = crypto.randomBytes(16); // Initialization vector

// Encrypt function
function encrypt(text) {
  const cipher = crypto.createCipheriv('aes-256-cbc', encryptionKey, iv);
  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted  = cipher.final('hex');
  return `${iv.toString('hex')}:${encrypted}`; // Store both IV and encrypted text
}

// Post request
app.post('/user', (req, res) => {
  const { creditCard, email } = req.body;

  // Encrypt the name
  const encryptedCard = encrypt(creditCard);

  // Save encrypted  to the database

  const user = new User({
    creditCard: encryptedCard ,
    email: email
  });// example of sequelize queries

  user.save().then(() => {
    res.send('User saved successfully');
  }).catch(err => {
    res.status(500).send('Error saving user');
  });
});


When you need to display the real data (in this case the card number) , you can use decrypt function inside your GET request.

// Decrypt function
function decrypt(encryptedData) {
  const [ivHex, encryptedText] = encryptedData.split(':');
  const ivBuffer = Buffer.from(ivHex, 'hex');
  const decipher = crypto.createDecipheriv('aes-256-cbc', encryptionKey, ivBuffer);
  let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
  decrypted  = decipher.final('utf8');
  return decrypted;
}

//GET request
app.get('/user/:id', (req, res) => {
  const userId = req.params.id;
 User.findById(userId).then(user => {
    if (!user) return res.status(404).send('User not found');

    // Decrypt the user's card 
    const decryptedCard = decrypt(user.creditCard);

 res.json({
      creditCard: decryptedCard ,
      email: user.email
    });
  }).catch(err => {
    res.status(500).send('Error retrieving user');
  });
});
  • Security Headers:

-Apply HTTP security headers like Strict-Transport-Security, X-Frame-Options, X-XSS-Protection, and X-Content-Type-Options to protect against various common vulnerabilities.

The simplest way to implement it is by using the helmet middleware in express after installing it npm install helmet.

const express = require('express');
const helmet = require('helmet');
const app = express();

app.use(helmet());

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

The alternative implementation is to manually setting Headers without Helmet as shown as below.

app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self' https://trusted.cdn.com");
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Referrer-Policy', 'no-referrer');
  res.setHeader('Permissions-Policy', 'geolocation=(self), microphone=()');
  next();
});
  • CSRF Protection:

-Implement Cross-Site Request Forgery (CSRF) protection mechanisms such as CSRF tokens to prevent unauthorized actions in your app.

How CSRF Attacks Work:

-The user logs into your app, and the server sets a session cookie or an authentication token.
-The user then visits a malicious site while still logged in to your app in the background.
-The malicious site sends a request to your app using the logged-in user's credentials (session cookie) to perform unintended actions like transferring money or changing account details.

To prevent CSRF attacks, you need to ensure that requests made from your frontend are authenticated and originate from a trusted source. The most common way to protect against CSRF is by using CSRF tokens.

Implementation is easy by using cookie parser and express.urlencoded middlewares
first install them npm install csurf cookie-parser

const express = require('express');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const app = express();

// Use cookie-parser for CSRF token storage (if using cookie-based tokens)
app.use(cookieParser());

// Initialize the CSRF middleware
const csrfProtection = csrf({ cookie: true });

// Use body-parser to parse form data
app.use(express.urlencoded({ extended: false }));

// routes here after applying the middlewares

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
  • Logging and monitoring:

-Ensure proper logging of security-related events such as failed login attempts and potential XSS attacks. Also, set up monitoring and alerts for abnormal behavior.

Conclusion:

This article aimed to highlight and summarize the key security principles that should be considered when developing full-stack JavaScript applications. We also explored practical ways to implement these principles efficiently. Additionally, these insights can be valuable when preparing for job interviews, especially when discussing various security measures and best practices in app development.

版本声明 本文转载于:https://dev.to/oussamabouyahia/key-security-principales-for-frontend-and-backend-development-o14?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何使用 CSS 自定义文本下划线颜色?
    如何使用 CSS 自定义文本下划线颜色?
    使用 CSS 自定义文本下划线颜色在网页设计中,为文本添加下划线是强调或突出显示信息的常见做法。但是,如果您想通过更改下划线的颜色来添加独特的触感该怎么办?这可能吗?是的,可以使用 CSS 更改文本下方线条的颜色。您可以使用以下两种方法:方法 1:使用 text-decoration-color最直...
    编程 发布于2024-11-08
  • 在 JavaScript 中实现点击劫持防御技术
    在 JavaScript 中实现点击劫持防御技术
    点击劫持等复杂攻击的出现使安全成为当今网络世界的首要问题。通过欺骗消费者点击与他们最初看到的内容不同的内容,攻击者部署了一种名为“点击劫持”的邪恶方法,这可能会带来灾难性的后果。此类攻击有可能诱骗人们下载恶意软件、发送私人信息,甚至做他们无意的事情,例如购买任何东西。为了防止此类攻击,JavaScr...
    编程 发布于2024-11-08
  • 为什么我的浮动 Div 不调整后续 Div 的大小?
    为什么我的浮动 Div 不调整后续 Div 的大小?
    Float 不调整 Div 大小之谜当使用 CSS float 时,假设后续元素将左对齐而不是流到新的元素上线。然而,在某些情况下,例如提供的示例,下面的 div 继续跨越整个宽度,而不是从第一个 div 的右侧开始。为了理解这种行为,我们深入研究 float 的复杂性定位。当元素浮动时(在本例中为...
    编程 发布于2024-11-08
  • 使用 PYTHON 将数据导入 MYSQL
    使用 PYTHON 将数据导入 MYSQL
    介绍 手动将数据导入数据库,尤其是当数据库中有多个表时,不仅很烦人,而且还很耗时。通过使用 python 库可以使这变得更容易。 从kaggle下载绘画数据集。绘画数据集由 8 个 csv 文件组成,我们将使用简单的 python 脚本将其导入到数据库中,而不是手动将数据导入到数据...
    编程 发布于2024-11-08
  • MySQL 基本运算符及其应用
    MySQL 基本运算符及其应用
    MySQL 运算符是开发人员的关键工具,可实现精确的数据操作和分析。它们涵盖了一系列功能,包括赋值、数据比较和复杂模式匹配。无论您是处理 JSON 数据还是根据条件过滤记录,了解这些运算符对于高效的数据库管理都至关重要。 本指南介绍了最重要的MySQL运算符,并通过实际示例演示了如何使用它们,使开...
    编程 发布于2024-11-08
  • 如何测试 Cron 作业:完整指南
    如何测试 Cron 作业:完整指南
    Cron 作业在许多系统中对于调度任务、自动化流程和按指定时间间隔运行脚本至关重要。无论您是维护 Web 服务器、自动备份还是运行例行数据导入,cron 作业都能让您的操作顺利运行。但与任何自动化任务一样,它们必须经过彻底测试以确保可靠性和准确性。 在本文中,我们将探讨如何有效地测试 cron 作...
    编程 发布于2024-11-08
  • Next.js 中间件简介:它如何工作并提供示例
    Next.js 中间件简介:它如何工作并提供示例
    我们来谈谈Nextjs中的路由。今天,我们来谈谈最强大的事物中间件之一。 Nextjs 中的中间件提供了一种强大而灵活的方法来拦截来自服务器的请求并控制请求流(重定向、URL 重写)并全局增强身份验证、标头、cookie 持久性等功能。 创建中间件 让我们创建 Middleware ...
    编程 发布于2024-11-08
  • 道具基础知识:第 1 部分
    道具基础知识:第 1 部分
    这是一个关于如何使用道具的初学者友好教程。在阅读之前了解什么是解构以及如何使用/创建组件非常重要。 Props,properties的缩写,props允许我们从父组件向子组件发送信息,还需要注意的是它们可以是任何数据类型。 必须了解为任何组件创建 prop 的语法。在 React 中,您必须使用...
    编程 发布于2024-11-08
  • Hibernate 与 Spring Boot 有何不同?
    Hibernate 与 Spring Boot 有何不同?
    Hibernate 与 Spring Boot 有何不同? Hibernate 和 Spring Boot 都是 Java 生态系统中流行的框架,但它们有不同的用途并具有不同的功能。 休眠 Hibernate 是一个对象关系映射 (ORM) 框架,它允许开发人员使用...
    编程 发布于2024-11-08
  • C++ 如何处理十进制数据类型?
    C++ 如何处理十进制数据类型?
    C 中的十进制数据类型 C 提供了各种数据类型来处理数值,但令人惊讶的是,十进制数据类型本身并不支持。在处理精确的十进制值或与使用十进制格式的系统交互时,这可能是一个限制。实现选项虽然 C 不提供内置十进制类型,但有两种与他们合作的方法:1。 C Decimal TR 扩展:某些编译器(例如 gcc...
    编程 发布于2024-11-08
  • 为什么我的 Python 中的凯撒密码函数只显示最后一个移位的字符?
    为什么我的 Python 中的凯撒密码函数只显示最后一个移位的字符?
    Python 中的凯撒密码函数:加密字符串在 Python 中实现凯撒密码函数时,会出现一个常见问题,即最终的加密文本仅显示最后移动的字符。要解决此问题,有必要了解导致此行为的问题。在提供的代码中,循环迭代明文中的每个字符。对于字母字符,它根据提供的移位值来移位字符的 ASCII 代码。但是,每个移...
    编程 发布于2024-11-08
  • 4 快速​​部署PHP
    4 快速​​部署PHP
    Servbay 已成为轻松配置开发环境的首要工具。在本指南中,我们将演示如何快速、安全地部署 PHP 8.2,强调 Servbay 致力于简化部署过程。 先决条件 开始之前,请确保您的设备上安装了 Servbay。您可以直接从Servbay官方网站下载。安装直观;只需按照提示操作,就...
    编程 发布于2024-11-08
  • AngularJS 指令中的 Replace 属性何时被弃用?
    AngularJS 指令中的 Replace 属性何时被弃用?
    为什么 AngularJS 已弃用指令中的替换属性AngularJS 指令中的替换属性由于其复杂性和更好的出现而被弃用替代方案。根据官方 AngularJS API 文档,在未来的版本中它将默认为 false。弃用的原因AngularJS 团队发现了替换属性的几个问题:困难的语义: 它导致了属性合并...
    编程 发布于2024-11-08
  • 释放 Claude AI:用于经济实惠且灵活的 AI 集成的非官方 API
    释放 Claude AI:用于经济实惠且灵活的 AI 集成的非官方 API
    由 Anthropic 开发的 Claude AI 以其令人印象深刻的能力在人工智能界掀起了波澜。然而,官方 API 对于许多开发人员和小型企业来说可能过于昂贵。这就是我们的非官方 Claude AI API 的用武之地,它提供了一个更实惠、更灵活的解决方案,将 Claude 的力量集成到您的项目中...
    编程 发布于2024-11-08
  • 如何使用时间包确定 Go 中一个月的最后一天?
    如何使用时间包确定 Go 中一个月的最后一天?
    使用 Time.Time 确定给定月份的最后一天处理基于时间的数据时,通常需要确定指定月份的最后一天。无论该月有 28 天、29 天(闰年)还是 30 天或 31 天,这都会使这成为一项具有挑战性的任务。时间包解决方案Go 时间包其日期函数提供了一个方便的解决方案。 Date 的语法为:func D...
    编程 发布于2024-11-08

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3