」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 前端和後端開發的關鍵安全原則

前端和後端開發的關鍵安全原則

發佈於2024-11-03
瀏覽:441

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]刪除
最新教學 更多>
  • 使用jQuery如何有效修改":after"偽元素的CSS屬性?
    使用jQuery如何有效修改":after"偽元素的CSS屬性?
    在jquery中了解偽元素的限制:訪問“ selector 嘗試修改“:”選擇器的CSS屬性時,您可能會遇到困難。 This is because pseudo-elements are not part of the DOM (Document Object Model) and are th...
    程式設計 發佈於2025-07-06
  • 您可以使用CSS在Chrome和Firefox中染色控制台輸出嗎?
    您可以使用CSS在Chrome和Firefox中染色控制台輸出嗎?
    在javascript console 中顯示顏色是可以使用chrome的控制台顯示彩色文本,例如紅色的redors,for for for for錯誤消息? 回答是的,可以使用CSS將顏色添加到Chrome和Firefox中的控制台顯示的消息(版本31或更高版本)中。要實現這一目標,請使用以下...
    程式設計 發佈於2025-07-06
  • 如何檢查對像是否具有Python中的特定屬性?
    如何檢查對像是否具有Python中的特定屬性?
    方法來確定對象屬性存在尋求一種方法來驗證對像中特定屬性的存在。考慮以下示例,其中嘗試訪問不確定屬性會引起錯誤: >>> a = someClass() >>> A.property Trackback(最近的最新電話): 文件“ ”,第1行, AttributeError: SomeClass...
    程式設計 發佈於2025-07-06
  • 如何使用Python有效地以相反順序讀取大型文件?
    如何使用Python有效地以相反順序讀取大型文件?
    在python 反向行讀取器生成器 == ord('\ n'): 緩衝區=緩衝區[:-1] 剩餘_size- = buf_size lines = buffer.split('\ n'....
    程式設計 發佈於2025-07-06
  • 為什麼PHP的DateTime :: Modify('+1個月')會產生意外的結果?
    為什麼PHP的DateTime :: Modify('+1個月')會產生意外的結果?
    使用php dateTime修改月份:發現預期的行為在使用PHP的DateTime類時,添加或減去幾個月可能並不總是會產生預期的結果。正如文檔所警告的那樣,“當心”這些操作的“不像看起來那樣直觀。 ; $ date->修改('1個月'); //前進1個月 echo $ date->...
    程式設計 發佈於2025-07-06
  • 為什麼我的CSS背景圖像出現?
    為什麼我的CSS背景圖像出現?
    故障排除:CSS背景圖像未出現 ,您的背景圖像儘管遵循教程說明,但您的背景圖像仍未加載。圖像和样式表位於相同的目錄中,但背景仍然是空白的白色帆布。 而不是不棄用的,您已經使用了CSS樣式: bockent {背景:封閉圖像文件名:背景圖:url(nickcage.jpg); 如果您的html,cs...
    程式設計 發佈於2025-07-06
  • 如何避免Go語言切片時的內存洩漏?
    如何避免Go語言切片時的內存洩漏?
    ,a [j:] ...雖然通常有效,但如果使用指針,可能會導致內存洩漏。這是因為原始的備份陣列保持完整,這意味著新切片外部指針引用的任何對象仍然可能佔據內存。 copy(a [i:] 對於k,n:= len(a)-j i,len(a); k
    程式設計 發佈於2025-07-06
  • C++20 Consteval函數中模板參數能否依賴於函數參數?
    C++20 Consteval函數中模板參數能否依賴於函數參數?
    [ consteval函數和模板參數依賴於函數參數在C 17中,模板參數不能依賴一個函數參數,因為編譯器仍然需要對非contexexpr futcoriations contim at contexpr function進行評估。 compile time。 C 20引入恆定函數,必須在編譯時進...
    程式設計 發佈於2025-07-06
  • 在程序退出之前,我需要在C ++中明確刪除堆的堆分配嗎?
    在程序退出之前,我需要在C ++中明確刪除堆的堆分配嗎?
    在C中的顯式刪除 在C中的動態內存分配時,開發人員通常會想知道是否有必要在heap-procal extrable exit exit上進行手動調用“ delete”操作員,但開發人員通常會想知道是否需要手動調用“ delete”操作員。本文深入研究了這個主題。 在C主函數中,使用了動態分配變量(...
    程式設計 發佈於2025-07-06
  • 如何在Java字符串中有效替換多個子字符串?
    如何在Java字符串中有效替換多個子字符串?
    在java 中有效地替換多個substring,需要在需要替換一個字符串中的多個substring的情況下,很容易求助於重複應用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    程式設計 發佈於2025-07-06
  • 反射動態實現Go接口用於RPC方法探索
    反射動態實現Go接口用於RPC方法探索
    在GO 使用反射來實現定義RPC式方法的界面。例如,考慮一個接口,例如:鍵入myService接口{ 登錄(用戶名,密碼字符串)(sessionId int,錯誤錯誤) helloworld(sessionid int)(hi String,錯誤錯誤) } 替代方案而不是依靠反射...
    程式設計 發佈於2025-07-06
  • 如何使用Python理解有效地創建字典?
    如何使用Python理解有效地創建字典?
    在python中,詞典綜合提供了一種生成新詞典的簡潔方法。儘管它們與列表綜合相似,但存在一些顯著差異。 與問題所暗示的不同,您無法為鑰匙創建字典理解。您必須明確指定鍵和值。 For example:d = {n: n**2 for n in range(5)}This creates a dict...
    程式設計 發佈於2025-07-06
  • 如何使用組在MySQL中旋轉數據?
    如何使用組在MySQL中旋轉數據?
    在關係數據庫中使用mySQL組使用mySQL組進行查詢結果,在關係數據庫中使用MySQL組,轉移數據的數據是指重新排列的行和列的重排以增強數據可視化。在這裡,我們面對一個共同的挑戰:使用組的組將數據從基於行的基於列的轉換為基於列。 Let's consider the following ...
    程式設計 發佈於2025-07-06
  • 表單刷新後如何防止重複提交?
    表單刷新後如何防止重複提交?
    在Web開發中預防重複提交 在表格提交後刷新頁面時,遇到重複提交的問題是常見的。要解決這個問題,請考慮以下方法: 想像一下具有這樣的代碼段,看起來像這樣的代碼段:)){ //數據庫操作... 迴聲“操作完成”; 死(); } ? > ...
    程式設計 發佈於2025-07-06
  • 在PHP中如何高效檢測空數組?
    在PHP中如何高效檢測空數組?
    在PHP 中檢查一個空數組可以通過各種方法在PHP中確定一個空數組。如果需要驗證任何數組元素的存在,則PHP的鬆散鍵入允許對數組本身進行直接評估:一種更嚴格的方法涉及使用count()函數: if(count(count($ playerList)=== 0){ //列表為空。 } 對...
    程式設計 發佈於2025-07-06

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3