MST

星途 面试题库

面试题:C语言实现HTTP协议状态机及安全增强

使用C语言在Linux平台实现一个完整的HTTP协议状态机,处理不同类型的HTTP请求(GET、POST等),并考虑如何对其进行安全增强,如防止常见的HTTP攻击(如SQL注入、XSS等),详细描述设计思路、关键数据结构和状态转换逻辑。
40.0万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 整体架构:将HTTP协议状态机设计为有限状态自动机(FSM),每个状态对应HTTP请求处理的不同阶段。
  2. 请求解析:从网络套接字读取数据,按HTTP协议格式解析请求行、头部和正文(对于POST请求)。
  3. 请求处理:根据请求方法(GET、POST等),调用相应的处理函数处理业务逻辑。
  4. 安全增强:对输入数据进行严格验证和过滤,防止SQL注入和XSS攻击。

关键数据结构

  1. 状态结构体
typedef enum {
    STATE_START,
    STATE_METHOD,
    STATE_URI,
    STATE_VERSION,
    STATE_HEADER_NAME,
    STATE_HEADER_VALUE,
    STATE_BODY,
    STATE_COMPLETE
} HttpState;

typedef struct {
    HttpState state;
    char method[10];
    char uri[256];
    char version[10];
    char headers[1024];
    char body[4096];
} HttpRequest;
  1. 缓冲区:用于从套接字读取数据。
#define BUFFER_SIZE 4096
char buffer[BUFFER_SIZE];

状态转换逻辑

  1. STATE_START:开始读取请求,等待读取到请求方法,转换到STATE_METHOD。
if (sscanf(buffer, "%9s", request->method) == 1) {
    request->state = STATE_METHOD;
}
  1. STATE_METHOD:读取完方法后,读取URI,转换到STATE_URI。
if (sscanf(buffer + strlen(request->method), " %255s", request->uri) == 1) {
    request->state = STATE_URI;
}
  1. STATE_URI:读取完URI后,读取HTTP版本,转换到STATE_VERSION。
if (sscanf(buffer + strlen(request->method) + strlen(request->uri), " %9s", request->version) == 1) {
    request->state = STATE_VERSION;
}
  1. STATE_VERSION:读取完版本后,开始读取头部,转换到STATE_HEADER_NAME。
request->state = STATE_HEADER_NAME;
  1. STATE_HEADER_NAME:读取头部名称,转换到STATE_HEADER_VALUE。
// 解析头部名称逻辑,假设已找到头部名称
request->state = STATE_HEADER_VALUE;
  1. STATE_HEADER_VALUE:读取头部值,然后可能回到STATE_HEADER_NAME继续读取下一个头部,或在读取完所有头部后转换到STATE_BODY(如果有正文)或STATE_COMPLETE(如果无正文)。
// 解析头部值逻辑
if (strstr(buffer, "\r\n\r\n") != NULL) {
    if (strlen(buffer) > strlen(request->headers) + 4) {
        request->state = STATE_BODY;
    } else {
        request->state = STATE_COMPLETE;
    }
} else {
    request->state = STATE_HEADER_NAME;
}
  1. STATE_BODY:读取POST请求正文,转换到STATE_COMPLETE。
// 读取正文逻辑
request->state = STATE_COMPLETE;
  1. STATE_COMPLETE:请求解析完成,可调用相应处理函数。

安全增强

  1. SQL注入防范:对输入数据进行转义处理,使用参数化查询。
// 例如使用MySQL库
MYSQL *conn;
conn = mysql_init(NULL);
mysql_real_connect(conn, "localhost", "user", "password", "database", 0, NULL, 0);
MYSQL_STMT *stmt = mysql_stmt_init(conn);
const char *query = "SELECT * FROM users WHERE username =? AND password =?";
mysql_stmt_prepare(stmt, query, strlen(query));
// 绑定参数
MYSQL_BIND bind[2];
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = request->username;
bind[0].is_null = 0;
bind[0].length = strlen(request->username);
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = request->password;
bind[1].is_null = 0;
bind[1].length = strlen(request->password);
mysql_stmt_bind_param(stmt, bind);
mysql_stmt_execute(stmt);
  1. XSS防范:对输出到HTML的数据进行转义,将特殊字符转换为HTML实体。
#include <stdio.h>
#include <string.h>

void html_escape(const char *input, char *output) {
    while (*input) {
        switch (*input) {
            case '<': strcpy(output, "&lt;"); output += 4; break;
            case '>': strcpy(output, "&gt;"); output += 4; break;
            case '&': strcpy(output, "&amp;"); output += 5; break;
            case '"': strcpy(output, "&quot;"); output += 6; break;
            default: *output++ = *input; break;
        }
        input++;
    }
    *output = '\0';
}

示例代码(完整可运行示例,简化版,仅包含基本状态机和安全增强部分示例)

#include <stdio.h>
#include <string.h>

typedef enum {
    STATE_START,
    STATE_METHOD,
    STATE_URI,
    STATE_VERSION,
    STATE_HEADER_NAME,
    STATE_HEADER_VALUE,
    STATE_BODY,
    STATE_COMPLETE
} HttpState;

typedef struct {
    HttpState state;
    char method[10];
    char uri[256];
    char version[10];
    char headers[1024];
    char body[4096];
} HttpRequest;

void html_escape(const char *input, char *output) {
    while (*input) {
        switch (*input) {
            case '<': strcpy(output, "&lt;"); output += 4; break;
            case '>': strcpy(output, "&gt;"); output += 4; break;
            case '&': strcpy(output, "&amp;"); output += 5; break;
            case '"': strcpy(output, "&quot;"); output += 6; break;
            default: *output++ = *input; break;
        }
        input++;
    }
    *output = '\0';
}

int main() {
    HttpRequest request;
    request.state = STATE_START;
    char buffer[BUFFER_SIZE] = "GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\n";

    while (request.state != STATE_COMPLETE) {
        switch (request.state) {
            case STATE_START:
                if (sscanf(buffer, "%9s", request.method) == 1) {
                    request.state = STATE_METHOD;
                }
                break;
            case STATE_METHOD:
                if (sscanf(buffer + strlen(request.method), " %255s", request.uri) == 1) {
                    request.state = STATE_URI;
                }
                break;
            case STATE_URI:
                if (sscanf(buffer + strlen(request.method) + strlen(request.uri), " %9s", request.version) == 1) {
                    request.state = STATE_VERSION;
                }
                break;
            case STATE_VERSION:
                request.state = STATE_HEADER_NAME;
                break;
            case STATE_HEADER_NAME:
                // 解析头部名称逻辑,假设已找到头部名称
                request.state = STATE_HEADER_VALUE;
                break;
            case STATE_HEADER_VALUE:
                // 解析头部值逻辑
                if (strstr(buffer, "\r\n\r\n") != NULL) {
                    if (strlen(buffer) > strlen(request.headers) + 4) {
                        request.state = STATE_BODY;
                    } else {
                        request.state = STATE_COMPLETE;
                    }
                } else {
                    request.state = STATE_HEADER_NAME;
                }
                break;
            case STATE_BODY:
                // 读取正文逻辑
                request.state = STATE_COMPLETE;
                break;
            case STATE_COMPLETE:
                break;
        }
    }

    char escaped_uri[256];
    html_escape(request.uri, escaped_uri);
    printf("Escaped URI: %s\n", escaped_uri);

    return 0;
}

以上代码展示了一个简单的HTTP协议状态机实现及安全增强示例,实际应用中需要结合网络编程等知识进行完善。