Improve security

This commit is contained in:
nexryai
2026-01-09 12:53:17 +00:00
committed by GitHub
parent 7d0af73c82
commit 5e0c77766b
2 changed files with 83 additions and 4 deletions
+81 -3
View File
@@ -6,9 +6,8 @@
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
try {
const isValid = await verifySignature(request, env);
if (!isValid) {
return new Response("Invalid Signature", { status: 403 });
if (request.method === "OPTIONS") {
return new Response(null, { status: 204 });
}
const url = new URL(request.url);
@@ -19,6 +18,19 @@ export default {
const bucket = pathParts[0] || "";
const objectKey = pathParts.slice(1).join("/");
if (!isValidPath(bucket, objectKey)) {
return new Response("Invalid path", { status: 400 });
}
if (!isAllowedBucket(bucket, env)) {
return new Response("Access denied to this bucket", { status: 403 });
}
const isValid = await verifySignature(request, env);
if (!isValid) {
return new Response("Invalid Signature", { status: 403 });
}
const accessToken = await getAccessToken(env);
if (method === "PUT" || method === "POST") {
@@ -125,6 +137,7 @@ interface Env {
GOOGLE_REFRESH_TOKEN: string;
AUTH_KV: KVNamespace;
FOLDER_CACHE: KVNamespace;
ALLOWED_BUCKETS?: string;
}
interface GoogleDriveFile {
@@ -139,6 +152,71 @@ interface GoogleDriveSearchResponse {
files?: GoogleDriveFile[];
}
// ========================================
// Security Functions
// ========================================
function isValidPath(bucket: string, objectKey: string): boolean {
// バケット名の検証
if (!bucket || bucket.includes("..") || bucket.includes("/") || bucket.includes("\\")) {
return false;
}
// オブジェクトキーの検証
if (objectKey) {
// ".." を含むパスを拒否
if (objectKey.includes("..")) {
return false;
}
// バックスラッシュを含むパスを拒否 (Windowsスタイルのパス)
if (objectKey.includes("\\")) {
return false;
}
// 絶対パスを拒否
if (objectKey.startsWith("/")) {
return false;
}
// パスの各コンポーネントを検証
const parts = objectKey.split("/");
for (const part of parts) {
// 空のコンポーネントや "." を拒否
if (!part || part === "." || part === "..") {
return false;
}
// NULLバイトを拒否
if (part.includes("\0")) {
return false;
}
}
}
return true;
}
function isAllowedBucket(bucket: string, env: Env): boolean {
console.log(bucket);
// 許可リストが設定されていない場合はすべて拒否
if (!env.ALLOWED_BUCKETS) {
return false;
}
const allowedBuckets = env.ALLOWED_BUCKETS.split(",")
.map((b) => b.trim())
.filter((b) => b);
// 空の許可リストの場合もすべて拒否
if (allowedBuckets.length === 0) {
return false;
}
// バケット名が許可リストに含まれているかチェック
return allowedBuckets.includes(bucket);
}
// ========================================
// Google Drive API Functions
// ========================================
+2 -1
View File
@@ -14,6 +14,7 @@ const ENV = {
GOOGLE_REFRESH_TOKEN: "test-refresh-token",
AUTH_KV: createMockKV(),
FOLDER_CACHE: createMockKV(),
ALLOWED_BUCKETS: "test-bucket,empty-bucket,my-bucket",
};
const CTX = {
@@ -222,7 +223,7 @@ describe("S3 API Server with Google Drive Backend", () => {
const command = new GetObjectCommand({
Bucket: "my-bucket",
Key: "test.png",
Key: "my-bucket/test.png",
});
const url = await getSignedUrl(s3, command, { expiresIn: 3600 });