为了账号安全,请及时绑定邮箱和手机立即绑定

后端开发中的防御性编程:打造稳健安全的系统

标签:
架构 安全

在后端应用程序开发中,确保应用程序的安全性和稳定性是必不可少的。后端是应用程序的支柱,负责处理业务逻辑、存储数据和与外部系统交互。编写强大且可靠的代码对所有软件开发人员来说至关重要。然而,无论多么小心,仍然可能会遇到bug和意外情况。这时,防御性编程就显得尤为重要了。

防御性编程是一种编码方式,旨在确保软件在出现意外事件或无效输入时仍能正确运行。对于后端开发者而言,防御性编程是一种重要的方法,它使我们能够设计出能够应对错误输入、系统错误和外部攻击的应用程序。

在这篇文章中,我们将讨论几种如何通过防守式编程来增强后端应用程序弹性的几种方式,并通过 Go 语言示例来说明如何实现这些方法,不过这些方法的应用不限于任何特定语言。

外部输入数据的校验

比如第三方API用户或数据库这样的外部来源可能是各种攻击的入口点,特别是当输入无效或带有恶意时。因为没有对输入进行适当的过滤,经常会遇到SQL注入、XSS攻击和命令注入等攻击。比如,我们可以采取以下措施来克服这些攻击。

1. 输入清理

来自外部来源的任何输入都必须经过净化处理。输入净化处理的过程是确保从用户或外部来源接收到的数据在被应用程序使用之前清除恶意字符,以确保数据的安全性和可靠性,尤其是在数据用于SQL查询、系统命令或输出到浏览器时。例如,如果您从表单接收到字符串输入,避免在未经净化的情况下直接将该输入插入SQL查询。您可以使用预编译语句来防止SQL注入攻击。使用预编译语句时,用户输入不能作为SQL命令的一部分,而是作为安全的参数。我们使用输入来检查用户的_username_和password,以验证他们的登录尝试。

    query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", "john_doe", "12345")
    rows, err := db.Query(query)

进入全屏。退出全屏。

最好使用预准备语句,这样可以确保用户输入永远不会被当作SQL查询的一部分来执行。输入被视为一个值,而不是SQL命令的一部分来执行,如下所示:

    // 预处理语句
    stmt, err := db.Prepare("SELECT * FROM users WHERE username = ? AND password = ?")
    if err != nil {
        log.Printf("预处理语句时出错: %v", err)
        return
    }
    defer stmt.Close()

    // 执行语句
    username := "john_doe"
    password := "12345"
    rows, err := stmt.Query(username, password)
    if err != nil {
        log.Printf("执行查询错误: %v", err)
        return
    }
    defer rows.Close()

点击进入全屏模式, 点击退出全屏模式

2. 数据类型验证.

数据类型的验证是指检查并确保接收到的数据类型符合预期,以便在进一步处理前使用。这很重要,可以防止数据处理中的错误并减少安全风险,例如SQL注入和XSS。例如,如果API期望接收的是电子邮件格式的数据,则需要验证输入是否为电子邮件格式。以下是一个使用Golang的数据类型验证示例:

    func main() {
        input := "hello@world.com"

        // 使用正则表达式检查电子邮件格式
        re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}# 后端开发中的防御性编程:打造稳健安全的系统

在后端应用程序开发中,确保应用程序的安全性和稳定性是必不可少的。后端是应用程序的支柱,负责处理业务逻辑、存储数据和与外部系统交互。编写强大且可靠的代码对所有软件开发人员来说至关重要。然而,无论多么小心,仍然可能会遇到bug和意外情况。这时,防御性编程就显得尤为重要了。

防御性编程是一种编码方式,旨在确保软件在出现意外事件或无效输入时仍能正确运行。对于后端开发者而言,防御性编程是一种重要的方法,它使我们能够设计出能够应对错误输入、系统错误和外部攻击的应用程序。

在这篇文章中,我们将讨论几种如何通过防守式编程来增强后端应用程序弹性的几种方式,并通过 Go 语言示例来说明如何实现这些方法,不过这些方法的应用不限于任何特定语言。

## 外部输入数据的校验

比如第三方API用户或数据库这样的外部来源可能是各种攻击的入口点,特别是当输入无效或带有恶意时。因为没有对输入进行适当的过滤,经常会遇到SQL注入、XSS攻击和命令注入等攻击。比如,我们可以采取以下措施来克服这些攻击。

### 1. 输入清理

来自外部来源的任何输入都必须经过净化处理。输入净化处理的过程是确保从用户或外部来源接收到的数据在被应用程序使用之前清除恶意字符,以确保数据的安全性和可靠性,尤其是在数据用于SQL查询、系统命令或输出到浏览器时。例如,如果您从表单接收到字符串输入,避免在未经净化的情况下直接将该输入插入SQL查询。您可以使用**预编译语句**来防止SQL注入攻击。使用预编译语句时,用户输入不能作为SQL命令的一部分,而是作为安全的参数。我们使用输入来检查用户的_username_和_password_,以验证他们的登录尝试。
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", "john_doe", "12345")
rows, err := db.Query(query)

进入全屏。退出全屏。

最好使用预准备语句,这样可以确保用户输入永远不会被当作SQL查询的一部分来执行。输入被视为一个值,而不是SQL命令的一部分来执行,如下所示:
// 预处理语句
stmt, err := db.Prepare("SELECT * FROM users WHERE username = ? AND password = ?")
if err != nil {
    log.Printf("预处理语句时出错: %v", err)
    return
}
defer stmt.Close()

// 执行语句
username := "john_doe"
password := "12345"
rows, err := stmt.Query(username, password)
if err != nil {
    log.Printf("执行查询错误: %v", err)
    return
}
defer rows.Close()

点击进入全屏模式, 点击退出全屏模式

### 2. 数据类型验证.

数据类型的验证是指检查并确保接收到的数据类型符合预期,以便在进一步处理前使用。这很重要,可以防止数据处理中的错误并减少安全风险,例如SQL注入和XSS。例如,如果API期望接收的是电子邮件格式的数据,则需要验证输入是否为电子邮件格式。以下是一个使用Golang的数据类型验证示例:

)
        if !re.MatchString(input) {
            fmt.Println("这不是一个有效的电子邮件地址。")
            return
        }

        fmt.Println("这是一个有效的电子邮件地址:", input)
    }

全屏 退出全屏

我们在这里使用正则表达式来验证输入是否符合邮箱格式要求。

3. 白名单和黑名单

白名单和黑名单是用于验证和管理应用程序中用户输入的两种方法。它们都旨在提高安全性和防止漏洞,但它们的工作方式及应用方式各有不同。

白名单

白名单是一种策略或方式,其中只有特定的数据或字符被应用程序处理。在处理用户输入时,白名单定义了哪些被视为“安全”的输入,并仅接受符合这些标准的输入。它通常用于验证特定的数据。使用示例:

    func isValidUsername(username string) bool {
        // 使用正则表达式验证用户名是否合法
        re := regexp.MustCompile(`^[a-zA-Z0-9_-]+# 后端开发中的防御性编程:打造稳健安全的系统

在后端应用程序开发中,确保应用程序的安全性和稳定性是必不可少的。后端是应用程序的支柱,负责处理业务逻辑、存储数据和与外部系统交互。编写强大且可靠的代码对所有软件开发人员来说至关重要。然而,无论多么小心,仍然可能会遇到bug和意外情况。这时,防御性编程就显得尤为重要了。

防御性编程是一种编码方式,旨在确保软件在出现意外事件或无效输入时仍能正确运行。对于后端开发者而言,防御性编程是一种重要的方法,它使我们能够设计出能够应对错误输入、系统错误和外部攻击的应用程序。

在这篇文章中,我们将讨论几种如何通过防守式编程来增强后端应用程序弹性的几种方式,并通过 Go 语言示例来说明如何实现这些方法,不过这些方法的应用不限于任何特定语言。

## 外部输入数据的校验

比如第三方API用户或数据库这样的外部来源可能是各种攻击的入口点,特别是当输入无效或带有恶意时。因为没有对输入进行适当的过滤,经常会遇到SQL注入、XSS攻击和命令注入等攻击。比如,我们可以采取以下措施来克服这些攻击。

### 1. 输入清理

来自外部来源的任何输入都必须经过净化处理。输入净化处理的过程是确保从用户或外部来源接收到的数据在被应用程序使用之前清除恶意字符,以确保数据的安全性和可靠性,尤其是在数据用于SQL查询、系统命令或输出到浏览器时。例如,如果您从表单接收到字符串输入,避免在未经净化的情况下直接将该输入插入SQL查询。您可以使用**预编译语句**来防止SQL注入攻击。使用预编译语句时,用户输入不能作为SQL命令的一部分,而是作为安全的参数。我们使用输入来检查用户的_username_和_password_,以验证他们的登录尝试。
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", "john_doe", "12345")
rows, err := db.Query(query)

进入全屏。退出全屏。

最好使用预准备语句,这样可以确保用户输入永远不会被当作SQL查询的一部分来执行。输入被视为一个值,而不是SQL命令的一部分来执行,如下所示:
// 预处理语句
stmt, err := db.Prepare("SELECT * FROM users WHERE username = ? AND password = ?")
if err != nil {
    log.Printf("预处理语句时出错: %v", err)
    return
}
defer stmt.Close()

// 执行语句
username := "john_doe"
password := "12345"
rows, err := stmt.Query(username, password)
if err != nil {
    log.Printf("执行查询错误: %v", err)
    return
}
defer rows.Close()

点击进入全屏模式, 点击退出全屏模式

### 2. 数据类型验证.

数据类型的验证是指检查并确保接收到的数据类型符合预期,以便在进一步处理前使用。这很重要,可以防止数据处理中的错误并减少安全风险,例如SQL注入和XSS。例如,如果API期望接收的是电子邮件格式的数据,则需要验证输入是否为电子邮件格式。以下是一个使用Golang的数据类型验证示例:
func main() {
    input := "hello@world.com"

    // 使用正则表达式检查电子邮件格式
    re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}# 后端开发中的防御性编程:打造稳健安全的系统

在后端应用程序开发中,确保应用程序的安全性和稳定性是必不可少的。后端是应用程序的支柱,负责处理业务逻辑、存储数据和与外部系统交互。编写强大且可靠的代码对所有软件开发人员来说至关重要。然而,无论多么小心,仍然可能会遇到bug和意外情况。这时,防御性编程就显得尤为重要了。

防御性编程是一种编码方式,旨在确保软件在出现意外事件或无效输入时仍能正确运行。对于后端开发者而言,防御性编程是一种重要的方法,它使我们能够设计出能够应对错误输入、系统错误和外部攻击的应用程序。

在这篇文章中,我们将讨论几种如何通过防守式编程来增强后端应用程序弹性的几种方式,并通过 Go 语言示例来说明如何实现这些方法,不过这些方法的应用不限于任何特定语言。

外部输入数据的校验

比如第三方API用户或数据库这样的外部来源可能是各种攻击的入口点,特别是当输入无效或带有恶意时。因为没有对输入进行适当的过滤,经常会遇到SQL注入、XSS攻击和命令注入等攻击。比如,我们可以采取以下措施来克服这些攻击。

1. 输入清理

来自外部来源的任何输入都必须经过净化处理。输入净化处理的过程是确保从用户或外部来源接收到的数据在被应用程序使用之前清除恶意字符,以确保数据的安全性和可靠性,尤其是在数据用于SQL查询、系统命令或输出到浏览器时。例如,如果您从表单接收到字符串输入,避免在未经净化的情况下直接将该输入插入SQL查询。您可以使用预编译语句来防止SQL注入攻击。使用预编译语句时,用户输入不能作为SQL命令的一部分,而是作为安全的参数。我们使用输入来检查用户的_username_和password,以验证他们的登录尝试。

    query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", "john_doe", "12345")
    rows, err := db.Query(query)

进入全屏。退出全屏。

最好使用预准备语句,这样可以确保用户输入永远不会被当作SQL查询的一部分来执行。输入被视为一个值,而不是SQL命令的一部分来执行,如下所示:

    // 预处理语句
    stmt, err := db.Prepare("SELECT * FROM users WHERE username = ? AND password = ?")
    if err != nil {
        log.Printf("预处理语句时出错: %v", err)
        return
    }
    defer stmt.Close()

    // 执行语句
    username := "john_doe"
    password := "12345"
    rows, err := stmt.Query(username, password)
    if err != nil {
        log.Printf("执行查询错误: %v", err)
        return
    }
    defer rows.Close()

点击进入全屏模式, 点击退出全屏模式

2. 数据类型验证.

数据类型的验证是指检查并确保接收到的数据类型符合预期,以便在进一步处理前使用。这很重要,可以防止数据处理中的错误并减少安全风险,例如SQL注入和XSS。例如,如果API期望接收的是电子邮件格式的数据,则需要验证输入是否为电子邮件格式。以下是一个使用Golang的数据类型验证示例:

)
if !re.MatchString(input) {
fmt.Println("这不是一个有效的电子邮件地址。")
return
}

    fmt.Println("这是一个有效的电子邮件地址:", input)
}

全屏 退出全屏

我们在这里使用正则表达式来验证输入是否符合邮箱格式要求。

### 3\. 白名单和黑名单

白名单和黑名单是用于验证和管理应用程序中用户输入的两种方法。它们都旨在提高安全性和防止漏洞,但它们的工作方式及应用方式各有不同。

#### 白名单

白名单是一种策略或方式,其中只有特定的数据或字符被应用程序处理。在处理用户输入时,白名单定义了哪些被视为“安全”的输入,并仅接受符合这些标准的输入。它通常用于验证特定的数据。使用示例:

)
        return re.MatchString(username)
    }

    func main() {
        input := "user_name-123"
        if isValidUsername(input) {
            fmt.Println("合法用户名:", input)
        } else {
            fmt.Println("非法用户名.")
        }
    }

进入全屏 退出全屏

在上面的例子中,正则表达式 `^[a-zA-Z0-9_-]+# 后端开发中的防御性编程:打造稳健安全的系统

在后端应用程序开发中,确保应用程序的安全性和稳定性是必不可少的。后端是应用程序的支柱,负责处理业务逻辑、存储数据和与外部系统交互。编写强大且可靠的代码对所有软件开发人员来说至关重要。然而,无论多么小心,仍然可能会遇到bug和意外情况。这时,防御性编程就显得尤为重要了。

防御性编程是一种编码方式,旨在确保软件在出现意外事件或无效输入时仍能正确运行。对于后端开发者而言,防御性编程是一种重要的方法,它使我们能够设计出能够应对错误输入、系统错误和外部攻击的应用程序。

在这篇文章中,我们将讨论几种如何通过防守式编程来增强后端应用程序弹性的几种方式,并通过 Go 语言示例来说明如何实现这些方法,不过这些方法的应用不限于任何特定语言。

外部输入数据的校验

比如第三方API用户或数据库这样的外部来源可能是各种攻击的入口点,特别是当输入无效或带有恶意时。因为没有对输入进行适当的过滤,经常会遇到SQL注入、XSS攻击和命令注入等攻击。比如,我们可以采取以下措施来克服这些攻击。

1. 输入清理

来自外部来源的任何输入都必须经过净化处理。输入净化处理的过程是确保从用户或外部来源接收到的数据在被应用程序使用之前清除恶意字符,以确保数据的安全性和可靠性,尤其是在数据用于SQL查询、系统命令或输出到浏览器时。例如,如果您从表单接收到字符串输入,避免在未经净化的情况下直接将该输入插入SQL查询。您可以使用预编译语句来防止SQL注入攻击。使用预编译语句时,用户输入不能作为SQL命令的一部分,而是作为安全的参数。我们使用输入来检查用户的_username_和password,以验证他们的登录尝试。

    query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", "john_doe", "12345")
    rows, err := db.Query(query)

进入全屏。退出全屏。

最好使用预准备语句,这样可以确保用户输入永远不会被当作SQL查询的一部分来执行。输入被视为一个值,而不是SQL命令的一部分来执行,如下所示:

    // 预处理语句
    stmt, err := db.Prepare("SELECT * FROM users WHERE username = ? AND password = ?")
    if err != nil {
        log.Printf("预处理语句时出错: %v", err)
        return
    }
    defer stmt.Close()

    // 执行语句
    username := "john_doe"
    password := "12345"
    rows, err := stmt.Query(username, password)
    if err != nil {
        log.Printf("执行查询错误: %v", err)
        return
    }
    defer rows.Close()

点击进入全屏模式, 点击退出全屏模式

2. 数据类型验证.

数据类型的验证是指检查并确保接收到的数据类型符合预期,以便在进一步处理前使用。这很重要,可以防止数据处理中的错误并减少安全风险,例如SQL注入和XSS。例如,如果API期望接收的是电子邮件格式的数据,则需要验证输入是否为电子邮件格式。以下是一个使用Golang的数据类型验证示例:

    func main() {
        input := "hello@world.com"

        // 使用正则表达式检查电子邮件格式
        re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}# 后端开发中的防御性编程:打造稳健安全的系统

在后端应用程序开发中,确保应用程序的安全性和稳定性是必不可少的。后端是应用程序的支柱,负责处理业务逻辑、存储数据和与外部系统交互。编写强大且可靠的代码对所有软件开发人员来说至关重要。然而,无论多么小心,仍然可能会遇到bug和意外情况。这时,防御性编程就显得尤为重要了。

防御性编程是一种编码方式,旨在确保软件在出现意外事件或无效输入时仍能正确运行。对于后端开发者而言,防御性编程是一种重要的方法,它使我们能够设计出能够应对错误输入、系统错误和外部攻击的应用程序。

在这篇文章中,我们将讨论几种如何通过防守式编程来增强后端应用程序弹性的几种方式,并通过 Go 语言示例来说明如何实现这些方法,不过这些方法的应用不限于任何特定语言。

## 外部输入数据的校验

比如第三方API用户或数据库这样的外部来源可能是各种攻击的入口点,特别是当输入无效或带有恶意时。因为没有对输入进行适当的过滤,经常会遇到SQL注入、XSS攻击和命令注入等攻击。比如,我们可以采取以下措施来克服这些攻击。

### 1. 输入清理

来自外部来源的任何输入都必须经过净化处理。输入净化处理的过程是确保从用户或外部来源接收到的数据在被应用程序使用之前清除恶意字符,以确保数据的安全性和可靠性,尤其是在数据用于SQL查询、系统命令或输出到浏览器时。例如,如果您从表单接收到字符串输入,避免在未经净化的情况下直接将该输入插入SQL查询。您可以使用**预编译语句**来防止SQL注入攻击。使用预编译语句时,用户输入不能作为SQL命令的一部分,而是作为安全的参数。我们使用输入来检查用户的_username_和_password_,以验证他们的登录尝试。
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", "john_doe", "12345")
rows, err := db.Query(query)

进入全屏。退出全屏。

最好使用预准备语句,这样可以确保用户输入永远不会被当作SQL查询的一部分来执行。输入被视为一个值,而不是SQL命令的一部分来执行,如下所示:
// 预处理语句
stmt, err := db.Prepare("SELECT * FROM users WHERE username = ? AND password = ?")
if err != nil {
    log.Printf("预处理语句时出错: %v", err)
    return
}
defer stmt.Close()

// 执行语句
username := "john_doe"
password := "12345"
rows, err := stmt.Query(username, password)
if err != nil {
    log.Printf("执行查询错误: %v", err)
    return
}
defer rows.Close()

点击进入全屏模式, 点击退出全屏模式

### 2. 数据类型验证.

数据类型的验证是指检查并确保接收到的数据类型符合预期,以便在进一步处理前使用。这很重要,可以防止数据处理中的错误并减少安全风险,例如SQL注入和XSS。例如,如果API期望接收的是电子邮件格式的数据,则需要验证输入是否为电子邮件格式。以下是一个使用Golang的数据类型验证示例:

)
        if !re.MatchString(input) {
            fmt.Println("这不是一个有效的电子邮件地址。")
            return
        }

        fmt.Println("这是一个有效的电子邮件地址:", input)
    }

全屏 退出全屏

我们在这里使用正则表达式来验证输入是否符合邮箱格式要求。

3. 白名单和黑名单

白名单和黑名单是用于验证和管理应用程序中用户输入的两种方法。它们都旨在提高安全性和防止漏洞,但它们的工作方式及应用方式各有不同。

白名单

白名单是一种策略或方式,其中只有特定的数据或字符被应用程序处理。在处理用户输入时,白名单定义了哪些被视为“安全”的输入,并仅接受符合这些标准的输入。它通常用于验证特定的数据。使用示例:

    func isValidUsername(username string) bool {
        // 使用正则表达式验证用户名是否合法
        re := regexp.MustCompile(`^[a-zA-Z0-9_-]+# 后端开发中的防御性编程:打造稳健安全的系统

在后端应用程序开发中,确保应用程序的安全性和稳定性是必不可少的。后端是应用程序的支柱,负责处理业务逻辑、存储数据和与外部系统交互。编写强大且可靠的代码对所有软件开发人员来说至关重要。然而,无论多么小心,仍然可能会遇到bug和意外情况。这时,防御性编程就显得尤为重要了。

防御性编程是一种编码方式,旨在确保软件在出现意外事件或无效输入时仍能正确运行。对于后端开发者而言,防御性编程是一种重要的方法,它使我们能够设计出能够应对错误输入、系统错误和外部攻击的应用程序。

在这篇文章中,我们将讨论几种如何通过防守式编程来增强后端应用程序弹性的几种方式,并通过 Go 语言示例来说明如何实现这些方法,不过这些方法的应用不限于任何特定语言。

## 外部输入数据的校验

比如第三方API用户或数据库这样的外部来源可能是各种攻击的入口点,特别是当输入无效或带有恶意时。因为没有对输入进行适当的过滤,经常会遇到SQL注入、XSS攻击和命令注入等攻击。比如,我们可以采取以下措施来克服这些攻击。

### 1. 输入清理

来自外部来源的任何输入都必须经过净化处理。输入净化处理的过程是确保从用户或外部来源接收到的数据在被应用程序使用之前清除恶意字符,以确保数据的安全性和可靠性,尤其是在数据用于SQL查询、系统命令或输出到浏览器时。例如,如果您从表单接收到字符串输入,避免在未经净化的情况下直接将该输入插入SQL查询。您可以使用**预编译语句**来防止SQL注入攻击。使用预编译语句时,用户输入不能作为SQL命令的一部分,而是作为安全的参数。我们使用输入来检查用户的_username_和_password_,以验证他们的登录尝试。
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", "john_doe", "12345")
rows, err := db.Query(query)

进入全屏。退出全屏。

最好使用预准备语句,这样可以确保用户输入永远不会被当作SQL查询的一部分来执行。输入被视为一个值,而不是SQL命令的一部分来执行,如下所示:
// 预处理语句
stmt, err := db.Prepare("SELECT * FROM users WHERE username = ? AND password = ?")
if err != nil {
    log.Printf("预处理语句时出错: %v", err)
    return
}
defer stmt.Close()

// 执行语句
username := "john_doe"
password := "12345"
rows, err := stmt.Query(username, password)
if err != nil {
    log.Printf("执行查询错误: %v", err)
    return
}
defer rows.Close()

点击进入全屏模式, 点击退出全屏模式

### 2. 数据类型验证.

数据类型的验证是指检查并确保接收到的数据类型符合预期,以便在进一步处理前使用。这很重要,可以防止数据处理中的错误并减少安全风险,例如SQL注入和XSS。例如,如果API期望接收的是电子邮件格式的数据,则需要验证输入是否为电子邮件格式。以下是一个使用Golang的数据类型验证示例:
func main() {
    input := "hello@world.com"

    // 使用正则表达式检查电子邮件格式
    re := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}# 后端开发中的防御性编程:打造稳健安全的系统

在后端应用程序开发中,确保应用程序的安全性和稳定性是必不可少的。后端是应用程序的支柱,负责处理业务逻辑、存储数据和与外部系统交互。编写强大且可靠的代码对所有软件开发人员来说至关重要。然而,无论多么小心,仍然可能会遇到bug和意外情况。这时,防御性编程就显得尤为重要了。

防御性编程是一种编码方式,旨在确保软件在出现意外事件或无效输入时仍能正确运行。对于后端开发者而言,防御性编程是一种重要的方法,它使我们能够设计出能够应对错误输入、系统错误和外部攻击的应用程序。

在这篇文章中,我们将讨论几种如何通过防守式编程来增强后端应用程序弹性的几种方式,并通过 Go 语言示例来说明如何实现这些方法,不过这些方法的应用不限于任何特定语言。

外部输入数据的校验

比如第三方API用户或数据库这样的外部来源可能是各种攻击的入口点,特别是当输入无效或带有恶意时。因为没有对输入进行适当的过滤,经常会遇到SQL注入、XSS攻击和命令注入等攻击。比如,我们可以采取以下措施来克服这些攻击。

1. 输入清理

来自外部来源的任何输入都必须经过净化处理。输入净化处理的过程是确保从用户或外部来源接收到的数据在被应用程序使用之前清除恶意字符,以确保数据的安全性和可靠性,尤其是在数据用于SQL查询、系统命令或输出到浏览器时。例如,如果您从表单接收到字符串输入,避免在未经净化的情况下直接将该输入插入SQL查询。您可以使用预编译语句来防止SQL注入攻击。使用预编译语句时,用户输入不能作为SQL命令的一部分,而是作为安全的参数。我们使用输入来检查用户的_username_和password,以验证他们的登录尝试。

    query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s' AND password = '%s'", "john_doe", "12345")
    rows, err := db.Query(query)

进入全屏。退出全屏。

最好使用预准备语句,这样可以确保用户输入永远不会被当作SQL查询的一部分来执行。输入被视为一个值,而不是SQL命令的一部分来执行,如下所示:

    // 预处理语句
    stmt, err := db.Prepare("SELECT * FROM users WHERE username = ? AND password = ?")
    if err != nil {
        log.Printf("预处理语句时出错: %v", err)
        return
    }
    defer stmt.Close()

    // 执行语句
    username := "john_doe"
    password := "12345"
    rows, err := stmt.Query(username, password)
    if err != nil {
        log.Printf("执行查询错误: %v", err)
        return
    }
    defer rows.Close()

点击进入全屏模式, 点击退出全屏模式

2. 数据类型验证.

数据类型的验证是指检查并确保接收到的数据类型符合预期,以便在进一步处理前使用。这很重要,可以防止数据处理中的错误并减少安全风险,例如SQL注入和XSS。例如,如果API期望接收的是电子邮件格式的数据,则需要验证输入是否为电子邮件格式。以下是一个使用Golang的数据类型验证示例:

)
if !re.MatchString(input) {
fmt.Println("这不是一个有效的电子邮件地址。")
return
}

    fmt.Println("这是一个有效的电子邮件地址:", input)
}

全屏 退出全屏

我们在这里使用正则表达式来验证输入是否符合邮箱格式要求。

### 3\. 白名单和黑名单

白名单和黑名单是用于验证和管理应用程序中用户输入的两种方法。它们都旨在提高安全性和防止漏洞,但它们的工作方式及应用方式各有不同。

#### 白名单

白名单是一种策略或方式,其中只有特定的数据或字符被应用程序处理。在处理用户输入时,白名单定义了哪些被视为“安全”的输入,并仅接受符合这些标准的输入。它通常用于验证特定的数据。使用示例:

)
        return re.MatchString(username)
    }

    func main() {
        input := "user_name-123"
        if isValidUsername(input) {
            fmt.Println("合法用户名:", input)
        } else {
            fmt.Println("非法用户名.")
        }
    }

进入全屏 退出全屏

被用来限制用户名中可以包含的字符。只有字母、数字、下划线(_)和短横线(-)是允许的。

- 黑名单

黑名单是指你将某些不允许的数据或字符列出来。在这种情况下,所有未列入黑名单的输入都被认为是安全的,这种方法常用于难以预测所有安全字符的情况。例如:

    func isValidInput(input string) bool {
        // 列出黑名单字符
        blacklist := []string{"<", ">", "&"}

        for _, char := range blacklist {
            if strings.Contains(input, char) {
                return false
            }
        }
        return true
    }

    func main() {
        input := "hello & welcome"

        if isValidInput(input) {
            fmt.Println("输入有效:", input)
        } else {
            fmt.Println("输入无效.")
        }
    }

    // 检查输入是否包含黑名单中的字符

切换到全屏 退出全屏

在这里,我们使用黑名单来检查输入是否含有不需要的字符。如果存在这些字符,输入就会被认为是无效的。

4, 输入限制

输入限制就是指在应用程序里控制用户能输入多少数据的方法。目的是防止资源被过度使用,提高应用程序的性能,并防范潜在攻击,例如拒绝服务(DoS)攻击或SQL注入攻击。下面举个例子:

    func validateUsername(username string) bool {
        if len(username) > 20 {
            return false // 用户名太长
        }
        return true // 有效
    }

    func main() {
        输入 := "ThisIsAReallyLongUsername"

        if validateUsername(输入) {
            fmt.Println("有效用户名:", 输入)
        } else {
            fmt.Println("无效用户名:用户名长度不能超过20个字符。")
        }
    }

切换全屏

当然,上述的一些要点可以应用于任何编程语言中,并且可以使用这些广泛可用的库,从而使数据验证过程比上述某些例子中的手动验证过程更简单。

数据保护

敏感数据,比如个人资料或凭证,在存储和传输过程中都必须得到保护。我们可以采取以下措施来保护这些数据:

1. 数据加密技术

敏感数据,比如密码或信用卡信息,应该始终加密。加密是指使用加密算法和密钥,将可读的明文转换成不可读的密文的过程。加密的主要目的是确保,即使数据落入他人之手,没有正确的密钥也无法解读。

使用如AES等现代加密算法,不要使用MD5或SHA1这类过时的算法。

2. 使用 HTTPS 协议。

使用 HTTPS 确保客户端和服务器之间传输的数据都是安全加密的。HTTPS 是浏览器(客户端)与服务器之间用于数据传输的 HTTP 的安全版本,它是浏览器(客户端)与服务器之间用于数据传输的标准协议。

HTTPS通过使用TLS(传输层安全)或SSL(安全套接层)协议来加密网络传输的数据,从而增加了一层安全保护。这特别是在后端开发中尤为重要,因为它可以防止如黑客等第三方读取或篡改客户端和服务器之间传输的数据。

3. 分词与掩码

在某些情况下,可以使用标记化或数据掩码来降低数据泄露的风险。标记化数据掩码是保护后端系统敏感信息的常用数据安全技术。

令牌化是将敏感数据(如信用卡号)替换为无意义值(称为“令牌”)的过程。原始数据单独存储,并且只能通过访问管理令牌的系统将其链接回令牌。这很重要,因为在发生数据泄露时,如果泄露的数据只是令牌而不是实际数据,攻击的风险将大大降低,因为令牌在系统外没有实际价值。比如,在网上支付时,可以将信用卡号替换为令牌,这样就无需实际存储卡数据在服务器上。

数据掩码是指隐藏敏感数据的部分内容,使只有部分内容可见。这通常用于用户界面或报告中,以防止完整信息的暴露。这如何实现?在不需要完整数据的用户面前保护敏感信息。即使被盗的数据只是显示在UI或日志中,掩码后的数据也只会显示部分信息,因此风险会小很多。一个常见的例子是在显示信用卡号时隐藏部分数字,例如:**** **** **** 1234

错误处理与日志

在构建系统时,错误总是不可避免的,但如何处理这些错误决定了我们的应用程序是否能在不影响数据的情况下继续运行。良好的日志记录有助于追踪生产系统问题并发现潜在模式。以下是我们的一些实践做法:

1. 合适地处理错误

不要只是捕获错误而不采取任何行动或提供任何信息。最好记录错误并提供一个良好的备用方案,以便及时处理,以避免系统崩溃。比如,如果数据库连接失败,可以尝试重连或暂时切换到只读模式。

2. 细粒度的日志记录

记录关键事件,比如 API 请求、认证失败或数据库连接问题,要详细记录。但是,请记得不要在日志中记录敏感信息,如密码。

3. 劈木头

使用不同的日志级别,如INFO、WARNING和ERROR来区分不同类型的消息。更详细的日志让开发人员更容易诊断问题。

关于错误管理和日志的更详细的探讨和示例,我在这篇文章中详细讨论了:可观测性 - 为什么日志记录很重要

学会优雅地避开错误

崩溃是系统遇到意外错误时产生的崩溃反应。然而,通过更好地处理,可以避免或减少错误。以下策略可以帮助我们更优雅地处理错误,从而使系统更加稳定。

1. 超时时段

当后端应用程序访问外部服务或等待如数据库这样的资源时,使用超时非常重要。这可以避免代码陷入无法确定的等待状态,从而保持应用程序的性能并避免死锁。下面是一个访问外部服务时设置超时的示例:

    client := http.Client{
        Timeout: 5 * time.Second,  // 超时时间为5秒
    }

    resp, err := client.Get("https://example.com/api")
    if err != nil {
        fmt.Println("请求出错:", err)
        return
    }
    defer resp.Body.Close()

    fmt.Println("响应如下:", resp.Status)

切换到全屏模式 退出全屏

在这个示例中,我们将 HTTP 请求的超时设置为 5 秒。如果服务在 5 秒内没有回应,应用不会卡住,我们可以处理这个错误,比如宣布失败。

2. 检查资源是否存在

在访问数据库、文件或API等其他资源之前,始终确认它们的存在和可用性。这样可以避免应用程序向可能不存在的资源请求,从而节省时间和避免不必要的错误。这里有一个检查方式示例:

    func main() {
        filePath := "./data.txt"
        _, err := os.Stat(filePath) // 检查文件

        if os.IsNotExist(err) {
            fmt.Println("找不到文件 %s 。\n", filePath)
        } else {
            fmt.Println("文件 %s 存在。\n", filePath)
        }
    }

全屏显示 退出全屏

3. 备用

当系统执行某些操作时,可能会出现错误。例如,我们的系统在认证过程中会发送一次性验证码(OTP)。如果OTP发送失败或遇到错误时,我们可以采用备用方案,比如转向我们的备用服务商发送OTP。

通过实施上述策略,我们可以减少导致崩溃的错误,降低崩溃的频率,保持应用的良好性能,并提升用户的整体体验。

安全且可靠的API设计

我们可以看到,APIs充当应用程序或网站的骨干,提供动态数据支持。然而,由于它们被视为如此关键的骨干,API常常成为攻击的目标。因此,确保API既安全又难以被攻击是非常重要的。

1. 身份验证与权限管理

使用强认证(如OAuth或JWT)来确保只有授权的用户或系统才能访问我们的API。实现细粒度的权限控制,限制访问我们API的用户的访问权限。这可以防止恶意用户访问我们的数据资源。

2. 限流和节流

速率限制和流量控制对于控制API使用以及防止滥用或过载问题至关重要。速率限制的应用也在于防范诸如DDoS攻击或用户和机器人过度利用API等问题,同时确保API对所有用户的服务稳定和可用。速率限制通过设定的时间间隔限制来自API客户端的请求频率,确保所有用户公平且平等访问我们的资源。

高效利用资源

后端系统常常需要管理各种资源。有效的资源管理对于保持应用程序的稳定性和性能,至关重要。通过合理地使用资源,我们可以避免诸如资源泄漏和系统故障等问题。以下是一些高效使用资源的策略。

1. 连接池技术

连接池是一种技术,其中到诸如数据库等服务的多个连接会被重复利用,而不是每次应用程序需要访问时都创建一个新的连接。使用连接池,应用程序无需每次请求时都创建新的连接,而是从池中复用现有的连接。

为什么连接池很重要呢?每次与数据库或服务建立连接时,都会消耗时间和内存资源。频繁地打开和关闭连接会耗尽系统资源。通过重用现有连接,应用程序可以更快地响应,并减轻对数据库等外部服务的压力。

2. 文件管理和内存管理

在处理文件或内存分配时,确保在使用完毕后释放资源这一点尤为重要。如果不这样做,可能会导致资源泄露,最终导致应用程序内存耗尽或文件句柄用尽。

例如,在 Go 语言中,我们可以使用内置函数 defer 来确保文件、连接或其他资源在使用后总是被正确释放。同时,处理资源时要检查错误,这样即使出现问题,资源也能够被正确释放。

这里有一个小代码片段,展示了我们如何使用defer在完成某个过程后关闭并停止操作。defer是一个特定的编程概念。

    func main() {
        // 打开文件操作
        file, err := os.Open("example.txt")
        if err != nil {
            fmt.Println("打开文件时出错:", err)
            return
        }

        // 确保文件在处理完毕后关闭
        defer file.Close()

        // 处理文件(省略具体处理代码)...
    }

全屏 退出全屏

通过 defer 关键字,我们确保文件会在函数成功执行或发生错误时退出时关闭。这有助于避免因忘记关闭文件而导致的文件句柄泄露。

3. 异步操作(Async操作)

在应用程序执行繁重任务或慢速 I/O 操作(如访问大文件或数据库,或调用外部 API)时,异步运行是一种有效避免应用程序被阻塞的方法。执行同步操作的应用程序在等待繁重任务完成期间可能被阻塞,从而影响整体性能。因此,异步处理有时非常关键,因为它允许应用程序在等待 I/O 或网络任务完成的同时继续处理其他任务。

假设我们在数据输入后清空 Redis 缓存数据。缓存删除可以异步执行,这样不会拖慢主进程。这意味着应用程序可以立即继续执行其他代码,而无需等待 Redis 的响应。这样应用程序就能保持高效和响应迅速。

结果

作为后端开发者,防御性编程是确保我们构建的应用程序能正确处理错误、无效输入和外部威胁的关键。通过运用上述技术,我们可以确保构建的后端应用程序更安全、稳定和有韧性,以应对各种意想不到的情况。防御性编程帮助我们构建不仅运行良好,还能在最糟糕的情况下生存下来的系统。

当然,以上可能还有很多未提及的重要方面,你可以在以上的评论区提供意见或建议。

希望这对你有用,挥手告别。

参考
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消