Deno 是一个现代的后端 JavaScript/TypeScript 运行时,由 Node.js 的创始人 Ryan Dahl 创建。Deno 提供了许多内置的安全特性,使得后端开发更加安全可靠。
Deno 简介
特点
- 安全性:默认情况下禁用文件系统访问、网络访问等权限,必须显式授予。
- 模块化:支持 ES 模块语法,无需额外的模块加载器。
- 内置工具:提供了许多内置工具,如 HTTP 服务器、WebSocket 服务器等。
- 跨平台:支持 Windows、macOS 和 Linux。
TypeScript 简介
- 类型安全:提供强类型检查,避免运行时错误。
- 静态类型检查:在编译阶段发现类型错误,提高代码质量。
- 工具支持:丰富的 IDE 和编辑器支持,提高开发效率。
安全性基础
权限管理
- Deno 默认禁用所有权限,必须显式授予。
- 权限包括文件系统访问、网络访问、环境变量访问等。
代码隔离
- 每个模块都在沙箱环境中运行,防止恶意代码影响全局。
Deno 安装与基本使用
安装 Deno
bash
curl -fsSL https://deno.land/x/install/install.sh | sh
deno --version
创建项目
bash
mkdir my-deno-app
cd my-deno-app
编写基本代码
typescript
// main.ts
console.log("Hello, Deno!");
运行代码
bash
deno run main.ts
权限管理
默认权限
默认情况下,Deno 不允许任何权限。
bash
deno run --allow-read main.ts
显式授予权限
授予读权限。
bash
deno run --allow-read main.ts
权限组合
授予读和写权限。
bash
deno run --allow-read --allow-write main.ts
HTTP 服务器
创建 HTTP 服务器
typescript
// server.ts
import { serve } from "https://deno.land/std@0.188.0/http/server.ts";
const handler = (req: Request) => {
if (req.method === "GET") {
return new Response("Hello, World!");
}
return new Response("Method not allowed", { status: 405 });
};
serve(handler);
运行 HTTP 服务器
bash
deno run --allow-net server.ts
访问服务器
bash
curl http://localhost:8000
安全的 HTTP 请求
发送 HTTP 请求
typescript
// request.ts
import { serve } from "https://deno.land/std@0.188.0/http/server.ts";
import { fetch } from "https://deno.land/std@0.188.0/fetch/mod.ts";
const handler = async (req: Request) => {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
return new Response(JSON.stringify(data), { headers: { "Content-Type": "application/json" } });
};
serve(handler);
运行请求处理程序
bash
deno run --allow-net request.ts
访问请求处理程序
bash
curl http://localhost:8000
数据验证与类型安全
数据验证
typescript
// validator.ts
import { serve } from "https://deno.land/std@0.188.0/http/server.ts";
import { z } from "https://deno.land/x/zod@v3.21.4/mod.ts";
interface User {
id: number;
name: string;
email: string;
}
const userSchema = z.object({
id: z.number(),
name: z.string().min(1),
email: z.string().email(),
});
const handler = async (req: Request) => {
const url = new URL(req.url);
const params = Object.fromEntries(url.searchParams.entries());
const result = userSchema.safeParse(params);
if (!result.success) {
return new Response(result.error.message, { status: 400 });
}
const user: User = result.data;
return new Response(JSON.stringify(user), { headers: { "Content-Type": "application/json" } });
};
serve(handler);
运行验证处理程序
bash
deno run --allow-net validator.ts
访问验证处理程序
bash
curl "http://localhost:8000?id=1&name=John&email=john@example.com"
安全的数据库操作
连接数据库
typescript
// database.ts
import { serve } from "https://deno.land/std@0.188.0/http/server.ts";
import { sql } from "https://deno.land/x/sqlite@v3.4.2/mod.ts";
const db = sql.open(":memory:");
const createTable = `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
`;
db.query(createTable);
const insertUser = `
INSERT INTO users (name, email) VALUES (?, ?);
`;
const selectUsers = `
SELECT * FROM users;
`;
const handler = async (req: Request) => {
const url = new URL(req.url);
const name = url.searchParams.get("name");
const email = url.searchParams.get("email");
if (!name || !email) {
return new Response("Missing parameters", { status: 400 });
}
try {
db.query(insertUser, [name, email]);
const users = db.query(selectUsers).fetchAll();
return new Response(JSON.stringify(users), { headers: { "Content-Type": "application/json" } });
} catch (error) {
return new Response(error.message, { status: 500 });
}
};
serve(handler);
运行数据库处理程序
bash
deno run --allow-net --allow-read --allow-write database.ts
访问数据库处理程序
bash
curl "http://localhost:8000?name=John&email=john@example.com"
日志与错误处理
日志记录
typescript
// logger.ts
import { serve } from "https://deno.land/std@0.188.0/http/server.ts";
import { format } from "https://deno.land/std@0.188.0/datetime/mod.ts";
const handler = async (req: Request) => {
const now = format(new Date(), "yyyy-MM-dd HH:mm:ss");
console.log(`${now} - ${req.method} - ${req.url}`);
return new Response("OK");
};
serve(handler);
错误处理
typescript
// error-handler.ts
import { serve } from "https://deno.land/std@0.188.0/http/server.ts";
const handler = async (req: Request) => {
try {
// Perform some operation
return new Response("OK");
} catch (error) {
console.error(error);
return new Response(error.message, { status: 500 });
}
};
serve(handler);
安全的最佳实践
最小权限原则
只授予必要的权限。
bash
deno run --allow-net --allow-read main.ts
输入验证
对所有外部输入进行严格验证。
typescript
const userSchema = z.object({
id: z.number(),
name: z.string().min(1),
email: z.string().email(),
});
加密敏感数据
对敏感数据进行加密处理。
typescript
import { crypto } from "https://deno.land/std@0.188.0/crypto/mod.ts";
const secret = crypto.randomUUID();
const encryptedData = crypto.encrypt(secret, sensitiveData);
安全的数据库操作
使用参数化查询防止 SQL 注入。
typescript
const insertUser = `
INSERT INTO users (name, email) VALUES (?, ?);
`;
db.query(insertUser, [name, email]);
安全的会话管理
使用安全的会话管理机制。
typescript
import { serve, Cookie } from "https://deno.land/std@0.188.0/http/server.ts";
const handler = async (req: Request) => {
const sessionCookie = req.headers.get("Cookie");
let sessionId = null;
if (sessionCookie) {
const cookie = new Cookie(sessionCookie);
sessionId = cookie.get("session_id")?.value;
}
if (!sessionId) {
sessionId = crypto.randomUUID();
const cookie = new Cookie({
name: "session_id",
value: sessionId,
httpOnly: true,
secure: true,
sameSite: "strict",
});
return new Response("Session created", {
headers: { "Set-Cookie": cookie.toString() },
});
}
// Perform session-related operations
return new Response("OK");
};
serve(handler);
使用 HTTPS
确保所有通信都使用 HTTPS。
typescript
const server = serve({ port: 443, hostname: "example.com", secure: true });
CORS 策略
配置适当的 CORS 策略。
typescript
const handler = async (req: Request) => {
const origin = req.headers.get("Origin");
if (origin === "https://example.com") {
return new Response("OK", {
headers: { "Access-Control-Allow-Origin": origin },
});
}
return new Response("Not allowed", { status: 403 });
};
serve(handler);
安全的文件上传
对上传文件进行严格的验证和限制。
typescript
import { serve } from "https://deno.land/std@0.188.0/http/server.ts";
import { parseMultipartFormData } from "https://deno.land/std@0.188.0/mime/multipart/form_data/parser.ts";
const handler = async (req: Request) => {
const formData = await parseMultipartFormData(req, { maxFileSize: 1024 * 1024 * 5 }); // 5MB limit
if (!formData.has("file")) {
return new Response("File missing", { status: 400 });
}
const file = formData.get("file");
if (!file) {
return new Response("Invalid file", { status: 400 });
}
const fileName = file.name;
const fileSize = file.size;
const fileType = file.type;
if (fileType !== "image/jpeg" && fileType !== "image/png") {
return new Response("Unsupported file type", { status: 400 });
}
// Save the file securely
const filePath = `./uploads/${fileName}`;
const fileStream = Deno.openSync(filePath, { write: true, create: true });
await fileStream.write(file.data);
fileStream.close();
return new Response("File uploaded successfully");
};
serve(handler);
安全的环境变量管理
使用环境变量存储敏感信息。
typescript
const apiKey = Deno.env.get("API_KEY");
const apiSecret = Deno.env.get("API_SECRET");
定期审计与更新
定期进行安全审计,并及时更新依赖库。
bash
deno upgrade
安全的认证与授权
JWT认证
使用 JWT 进行用户认证。
typescript
import { serve } from "https://deno.land/std@0.188.0/http/server.ts";
import { jwt } from "https://deno.land/x/djwt@v2.2/mod.ts";
const secret = Deno.env.get("JWT_SECRET");
const handler = async (req: Request) => {
const authHeader = req.headers.get("Authorization");
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return new Response("Unauthorized", { status: 401 });
}
const token = authHeader.slice(7); // Remove "Bearer " prefix
try {
const decodedToken = await jwt.decode(token, { secret });
return new Response("Authenticated");
} catch (error) {
return new Response("Invalid token", { status: 401 });
}
};
serve(handler);
角色与权限
使用角色和权限进行细粒度控制。
typescript
const userRoles = {
admin: ["read", "write", "delete"],
user: ["read"],
};
const handler = async (req: Request) => {
const authHeader = req.headers.get("Authorization");
const token = authHeader.slice(7); // Remove "Bearer " prefix
const decodedToken = await jwt.decode(token, { secret });
const role = decodedToken.role;
const permissions = userRoles[role];
if (!permissions.includes("write")) {
return new Response("Permission denied", { status: 403 });
}
// Perform write operation
return new Response("Write successful");
};
serve(handler);