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

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

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

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]删除
最新教程 更多>
  • PHP SimpleXML解析带命名空间冒号的XML方法
    PHP SimpleXML解析带命名空间冒号的XML方法
    在php 很少,请使用该限制很大,很少有很高。例如:这种技术可确保可以通过遍历XML树和使用儿童()方法()方法的XML树和切换名称空间来访问名称空间内的元素。
    编程 发布于2025-05-07
  • 如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    如何将PANDAS DataFrame列转换为DateTime格式并按日期过滤?
    Transform Pandas DataFrame Column to DateTime FormatScenario:Data within a Pandas DataFrame often exists in various formats, including strings.使用时间数据时...
    编程 发布于2025-05-07
  • 如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求和假用户代理绕过网站块?
    如何使用Python的请求模拟浏览器行为,以及伪造的用户代理提供了一个用户 - 代理标头一个有效方法是提供有效的用户式header,以提供有效的用户 - 设置,该标题可以通过browser和Acterner Systems the equestersystermery和操作系统。通过模仿像Chro...
    编程 发布于2025-05-07
  • 找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    如何在mySQL中使用mySql 检索最大计数,您可能会遇到一个问题,您可能会在尝试使用以下命令:理解错误正确找到由名称列分组的值的最大计数,请使用以下修改后的查询: 计数(*)为c 来自EMP1 按名称组 c desc订购 限制1 查询说明 select语句提取名称列和每个名称...
    编程 发布于2025-05-07
  • 如何使用Python理解有效地创建字典?
    如何使用Python理解有效地创建字典?
    在python中,词典综合提供了一种生成新词典的简洁方法。尽管它们与列表综合相似,但存在一些显着差异。与问题所暗示的不同,您无法为钥匙创建字典理解。您必须明确指定键和值。 For example:d = {n: n**2 for n in range(5)}This creates a dicti...
    编程 发布于2025-05-07
  • 在PHP中如何高效检测空数组?
    在PHP中如何高效检测空数组?
    在PHP 中检查一个空数组可以通过各种方法在PHP中确定一个空数组。如果需要验证任何数组元素的存在,则PHP的松散键入允许对数组本身进行直接评估:一种更严格的方法涉及使用count()函数: if(count(count($ playerList)=== 0){ //列表为空。 } 对...
    编程 发布于2025-05-07
  • 如何将多种用户类型(学生,老师和管理员)重定向到Firebase应用中的各自活动?
    如何将多种用户类型(学生,老师和管理员)重定向到Firebase应用中的各自活动?
    Red: How to Redirect Multiple User Types to Respective ActivitiesUnderstanding the ProblemIn a Firebase-based voting app with three distinct user type...
    编程 发布于2025-05-07
  • 用户本地时间格式及时区偏移显示指南
    用户本地时间格式及时区偏移显示指南
    在用户的语言环境格式中显示日期/时间,并使用时间偏移在向最终用户展示日期和时间时,以其localzone and格式显示它们至关重要。这确保了不同地理位置的清晰度和无缝用户体验。以下是使用JavaScript实现此目的的方法。方法:推荐方法是处理客户端的Javascript中的日期/时间格式化和时...
    编程 发布于2025-05-07
  • 您如何在Laravel Blade模板中定义变量?
    您如何在Laravel Blade模板中定义变量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配变量对于存储以后使用的数据至关重要。在使用“ {{}}”分配变量的同时,它可能并不总是最优雅的解决方案。幸运的是,Blade通过@php Directive提供了更优雅的方法: $ old_section =“...
    编程 发布于2025-05-07
  • 解决MySQL插入Emoji时出现的\\"字符串值错误\\"异常
    解决MySQL插入Emoji时出现的\\"字符串值错误\\"异常
    Resolving Incorrect String Value Exception When Inserting EmojiWhen attempting to insert a string containing emoji characters into a MySQL database us...
    编程 发布于2025-05-07
  • 我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    将我的加密库从mcrypt升级到openssl 问题:是否可以将我的加密库从McRypt升级到OpenSSL?如果是这样,如何?答案:是的,可以将您的Encryption库从McRypt升级到OpenSSL。可以使用openssl。附加说明: [openssl_decrypt()函数要求iv参...
    编程 发布于2025-05-07
  • 为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    查询模式实现缺失:解决“无法找到”错误在Silverlight应用程序中,尝试使用LINQ建立LINQ连接以错误而实现的数据库”,无法找到查询模式的实现。”当省略LINQ名称空间或查询类型缺少IEnumerable 实现时,通常会发生此错误。 解决问题来验证该类型的质量是至关重要的。在此特定实例中...
    编程 发布于2025-05-07
  • 如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    为有效的slug生成首先,该函数用指定的分隔符替换所有非字母或数字字符。此步骤可确保slug遵守URL惯例。随后,它采用ICONV函数将文本简化为us-ascii兼容格式,从而允许更广泛的字符集合兼容性。接下来,该函数使用正则表达式删除了不需要的字符,例如特殊字符和空格。此步骤可确保slug仅包含...
    编程 发布于2025-05-07
  • FastAPI自定义404页面创建指南
    FastAPI自定义404页面创建指南
    response = await call_next(request) if response.status_code == 404: return RedirectResponse("https://fastapi.tiangolo.com") else: ...
    编程 发布于2025-05-07
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,以便更快地搜索这些前缀。理解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-05-07

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

Copyright© 2022 湘ICP备2022001581号-3