@2026-09-28 15:01:19 - Calle 15 de Febrero

Calle 15 de Febrero
บทคัดย่อ ระบบลงคะแนนเสียงที่ปลอดภัย Fiveo1 เป็นระบบลงคะแนนแบบไฟล์เดียว โฮสต์เองได้ และเน้นการใช้งานบนมือถือเป็นหลัก โดยเชื่อมโยงการลงคะแนนใช่/ไม่ใช่แต่ละครั้งเข้ากับลายเซ็นเข้ารหัสลับ — แฮช SHA-256 ของที่อยู่เครือข่ายของผู้ลงคะแนน ลายนิ้วมือของอุปกรณ์ รูปทรงหน้าจอ และตำแหน่ง GPS ลายเซ็นจะถูกคำนวณเฉพาะบนเซิร์ฟเวอร์ จัดเก็บไว้ในฐานข้อมูล SQLite ในเครื่อง และบังคับใช้โดยข้อจำกัด SQL UNIQUE(question_id, signature) เมื่อลายเซ็นลงคะแนนให้กับบุคคลใดแล้ว ลายเซ็นนั้นจะไม่สามารถลงคะแนนให้กับบุคคลนั้นได้อีก — ไม่ว่าจะล้างแคช ลบคุกกี้ ติดตั้งเบราว์เซอร์ใหม่ หรือรีเซ็ตเซสชันก็ตาม การเคลื่อนไหวของ GPS จะสร้างลายเซ็นใหม่ ทำให้ผู้ลงคะแนนสามารถลงคะแนนใหม่ได้จากตำแหน่งทางกายภาพที่แตกต่างกันอย่างแท้จริง การลงคะแนนแต่ละครั้งจะถูกจัดเก็บเป็นรายการบรรทัดที่สมบูรณ์และไม่สามารถเปลี่ยนแปลงได้ ซึ่งประกอบด้วย IP อุปกรณ์ หน้าจอ พิกัด GPS ตำแหน่งที่แปลงเป็นพิกัดทางภูมิศาสตร์ เวลา และแฮชลายเซ็น แผนที่ OpenStreetMap แบบสะสมจะแสดงร่องรอยที่บันทึกไว้แต่ละรายการเป็นเครื่องหมายสีเขียว (ใช่) หรือสีแดง (ไม่ใช่) --- 1. เป้าหมายการออกแบบ เป้าหมาย การดำเนินการ หนึ่งสิทธิ์ต่อคน ต่ออุปกรณ์ ต่อสถานที่ ตลอดไป ลายเซ็นที่คำนวณโดยเซิร์ฟเวอร์ + ข้อจำกัด UNIQUE ของ SQLite ไม่ต้องอาศัยความไว้วางใจจากฝั่งไคลเอ็นต์ ลายเซ็นคำนวณเฉพาะบนเซิร์ฟเวอร์โดยใช้ส่วนหัวที่เซิร์ฟเวอร์สามารถสังเกตได้ ป้องกันแคช/คุกกี้/ประวัติ การบังคับใช้จะอยู่ในฐานข้อมูล ไม่ใช่ในเบราว์เซอร์ ใช้งานได้เฉพาะบนมือถือ ไม่ใช้แป้นพิมพ์ ไม่ใช้เมาส์ ตัวควบคุมทั้งหมดมีขนาดเป้าหมายการแตะ ≥52px ไม่มีช่องป้อนข้อความบนหน้าเว็บสาธารณะ บันทึกข้อมูลการลงคะแนนทั้งหมด แต่ละแถวของการลงคะแนนจะจัดเก็บ IP, UA, หน้าจอ, GPS, ความแม่นยำ, ตำแหน่งที่แปลงเป็นพิกัดทางภูมิศาสตร์, เวลา และลายเซ็น การแสดงผลทางภูมิศาสตร์แบบสะสม Leaflet + OpenStreetMap, เครื่องหมายหนึ่งจุดต่อการลงคะแนน, กำหนดสีตามคำตอบ ไม่มีการพึ่งพาภายนอกใดๆ นอกเหนือจากไทล์ Leaflet ไฟล์ SQLite ไฟล์เดียว, ไฟล์ PHP ไฟล์เดียว, CDN เฉพาะสำหรับไลบรารีแผนที่ การจัดเตรียมฐานข้อมูลและสคีมาอัตโนมัติจะถูกสร้างขึ้นเมื่อโหลดหน้าเว็บครั้งแรก --- 2. สถาปัตยกรรมระบบ ``` ┌──────────────────────────────┐ ┌─────────────────────────────┐ │ โทรศัพท์ของผู้มีสิทธิเลือกตั้ง │ │ Apache / Nginx + PHP-FPM │ │ ─────────────────────── │ │ ─────────────────────── │ │ • เบราว์เซอร์บนมือถือ │ HTTPS │ • index.php (สาธารณะ) │ │ • API ระบุตำแหน่งทางภูมิศาสตร์ │ ─────► │ • admin.php (รหัสผ่าน) │ │ • Leaflet.js (CDN) │ │ │ │ • Nominatim การแปลงพิกัดทางภูมิศาสตร์แบบย้อนกลับ │ │ ┌──────────────┐ │ │ │ │ │ fiveo1.sqlite│ │ └────────────────────────────┘ │ │ (โหมด WAL) │ │ │ └─────────────┘ │ └──────────────────────────────┘ ``` เลเยอร์ส่วนประกอบ ความรับผิดชอบ index.php แสดงผลการ์ดรอยเท้าแบบเรียลไทม์ ผลรวม ฟีดบุคคล ปุ่มแผนที่ ให้บริการ API JSON สำหรับลายเซ็น การโหวต และจุดบนแผนที่ รับ POST การโหวต admin.php ตัวจัดการคำถามที่ต้องใช้รหัสผ่าน ดู/ลบคำถาม ล้างการโหวต ดูลายเซ็นล่าสุด fiveo1.sqlite ฐานข้อมูลแบบฝังไฟล์เดียว; โหมด WAL เพื่อความปลอดภัยในการอ่านพร้อมกัน Leaflet 1.9.4 การแสดงผลแผนที่ฝั่งไคลเอ็นต์; โหลดไทล์จาก OpenStreetMap Nominatim บริการแปลงพิกัดทางภูมิศาสตร์แบบย้อนกลับฟรี; แปลงละติจูด/ลองจิจูดเป็นชื่อถนนที่มนุษย์อ่านได้ --- 3. ลายเซ็นฟุตพรินต์ 3.1 คำจำกัดความ ลายเซ็นคือค่าแฮช SHA-256 ที่เข้ารหัสแบบเลขฐานสิบหกของห้าฟิลด์ คั่นด้วยเครื่องหมายไปป์: ``` signature = SHA256( ip | user_agent | screen | lat₃ | lon₃ ) ``` หมายเหตุแหล่งที่มาของส่วนประกอบ ip $_SERVER['REMOTE_ADDR'] ไม่เคยให้โดยไคลเอ็นต์; ไม่สามารถปลอมแปลงผ่าน TCP user_agent $_SERVER['HTTP_USER_AGENT'] ส่วนหัวที่ Apache/PHP ได้รับจริง screen Client screen.width × screen.height normalised to max×min Rotation-proof lat₃ ละติจูด GPS ของไคลเอ็นต์ ปัดเศษเป็นทศนิยม 3 ตำแหน่ง ความละเอียด ~110 ม. lon₃ ลองจิจูด GPS ของไคลเอ็นต์ ปัดเศษเป็นทศนิยม 3 ตำแหน่ง ความละเอียด ~110 ม. 3.2 เหตุใด GPS จึงถูกปัดเศษเป็นทศนิยม 3 ตำแหน่ง ที่ 4 ตำแหน่ง (~11 ม.) โทรศัพท์ที่อยู่กับที่จะเคลื่อนที่ 5–20 ม. ระหว่างการโหลดหน้าเว็บเนื่องจากความผันผวนของ GPS ทำให้เกิดลายเซ็นที่แตกต่างกันในแต่ละครั้งที่เข้าชมและทำให้การล็อกใช้งานไม่ได้ ที่ 3 ตำแหน่ง (~110 ม.) การเคลื่อนที่จริงจะถูกดูดซับ แต่ผู้ลงคะแนนที่ย้ายตำแหน่งทางกายภาพไปยังบล็อกอื่นจะสร้างลายเซ็นใหม่ที่แท้จริง 3.3 เหตุใดเซิร์ฟเวอร์จึงคำนวณลายเซ็น ต้นแบบก่อนหน้านี้ให้ไคลเอ็นต์คำนวณลายเซ็นและส่งไปยังเซิร์ฟเวอร์ วิธีนี้ล้มเหลวด้วยสองเหตุผล: 1. การตัดทอนที่ไม่ตรงกัน — หากเซิร์ฟเวอร์จัดเก็บ substr($UA, 0, 400) แต่ไคลเอนต์ทำการแฮช UA แบบเต็ม ค่าแฮชจะแตกต่างกันหลายพันบิต และการตรวจจับข้อมูลซ้ำจะไม่ทำงาน 2. ความสามารถในการแก้ไขของไคลเอนต์ — ไคลเอนต์ที่เป็นอันตรายสามารถแฮชอะไรก็ได้ตามต้องการและสร้างลายเซ็นได้ไม่จำกัดจำนวน การออกแบบในปัจจุบันช่วยขจัดทั้งสองอย่าง: เซิร์ฟเวอร์เป็นผู้มีอำนาจแต่เพียงผู้เดียวในเรื่องลายเซ็น ไคลเอนต์ส่งเฉพาะ screen, lat และ lon และรับลายเซ็นที่ได้กลับมาเพื่อแสดงผล ไคลเอนต์ไม่สามารถปลอมลายเซ็นได้เพราะไม่ทราบว่าเซิร์ฟเวอร์แฮช ip และ user_agent อย่างไร 3.4 ตารางความเสถียรของลายเซ็น สถานการณ์ IP UA Screen GPS₃ ลายเซ็น อนุญาตให้ลงคะแนนอีกครั้งหรือไม่? โทรศัพท์เครื่องเดิม สถานที่เดิม ล้างแคชแล้ว เหมือนเดิม เหมือนเดิม เหมือนเดิม เหมือนเดิม ❌ ถูกบล็อก โทรศัพท์เครื่องเดิม สถานที่เดิม เบราว์เซอร์ใหม่ เหมือนเดิม เหมือนเดิม เหมือนเดิม เหมือนเดิม ❌ ถูกบล็อก โทรศัพท์เครื่องเดิม ย้ายไป 500 เมตร เหมือนเดิม เหมือนเดิม เหมือนเดิม ต่างกัน ต่างกัน ✅ เพิ่มสายใหม่ โทรศัพท์เครื่องเดิม เปลี่ยน Wi-Fi เป็นเซลลูลาร์ ต่างกัน เหมือนเดิม เหมือนเดิม เหมือนเดิม ต่างกัน ✅ เพิ่มสายใหม่ โทรศัพท์เครื่องเดิม การอัปเดตระบบปฏิบัติการเปลี่ยนสตริง UA เหมือนเดิม ต่างกัน เหมือนเดิม เหมือนกัน ต่างกัน เพิ่มสายใหม่ โทรศัพท์เครื่องอื่น Wi-Fi เครื่องเดิม สถานที่เดิม เหมือนเดิม ต่างกัน ต่างกัน เหมือนกัน ต่างกัน เพิ่มสายใหม่ --- 4. โครงสร้างฐานข้อมูล ```sql CREATE TABLE questions ( id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE TABLE votes ( id INTEGER PRIMARY KEY AUTOINCREMENT, question_id INTEGER NOT NULL, signature TEXT NOT NULL, answer TEXT NOT NULL, -- 'yes' | 'no' ip TEXT, device TEXT,-- UA (ตัดเหลือ 400 ตัวอักษรสำหรับการจัดเก็บ) screen TEXT, lat REAL, lon REAL, accuracy REAL, -- ความแม่นยำของ GPS ในหน่วยเมตร location TEXT, -- ถนน/เมืองที่แปลงเป็น geocode แล้ว user_id TEXT, -- UUID ของ localStorage ที่คงอยู่ created_at TEXT NOT NULL, UNIQUE(question_id, signature) -- ★ ข้อจำกัดคีย์หลัก ); CREATE INDEX idx_votes_q ON votes(question_id); CREATE INDEX idx_votes_sig ON votes(question_id, signature); ``` คีย์หลัก ข้อจำกัด UNIQUE(question_id, signature) เป็นแกนหลักของความปลอดภัยของระบบ มันถูกบังคับใช้ในระดับเอ็นจิ้น SQLite ไม่ใช่ในโค้ดแอปพลิเคชัน แม้ว่าจะข้ามเลเยอร์แอปพลิเคชันไปได้ แต่ฐานข้อมูลเองก็จะปฏิเสธการแทรกข้อมูลซ้ำด้วยข้อผิดพลาด SQLITE_CONSTRAINT ซึ่งตัวจัดการ PHP จะแปลงเป็นข้อความตอบกลับ HTTP 409 duplicate_signature เมื่อมีแถวสำหรับคู่ (question_id, signature) ที่กำหนดแล้ว แถวนั้นจะเป็นคีย์หลัก จะไม่สามารถมีแถวที่สองที่มีคู่เดียวกันได้อีก การลงคะแนนเป็นโทเค็นถาวรที่ใช้ได้ครั้งเดียวและจำกัดขอบเขตสำหรับบุคคลนั้นโดยเฉพาะ --- 5. วงจรชีวิตการลงคะแนน 5.1 ก่อนการลงคะแนน (การตรวจสอบแบบเงียบ) ``` 1. โหลดหน้าเว็บ → ไคลเอนต์ร้องขอ ?action=client_ip → เซิร์ฟเวอร์ส่ง REMOTE_ADDR กลับมา 2. ไคลเอนต์ร้องขอ GPS ผ่าน navigator.geolocation.getCurrentPosition() 3. เมื่อล็อก GPS ได้แล้ว ไคลเอนต์ร้องขอ ?action=my_signature&screen=…&lat=…&lon=… 4. เซิร์ฟเวอร์คำนวณลายเซ็นใหม่และส่งคืน: { ok:true, signature:"abc…", voted:{ "12":"yes", "34":"no", … } } 5. ไคลเอนต์วนซ้ำ .person[data-qid] และสำหรับแต่ละ qid ใน voted: · ปิดใช้งานปุ่ม YES และ NO ทั้งสองปุ่ม · ทำเครื่องหมายปุ่มที่เลือกด้วย .chosen (สีเขียว/แดง) · แสดง "✔ รอยเท้าของคุณลงคะแนน YES ให้กับบุคคลนี้แล้ว" สำหรับ qid ที่ไม่อยู่ใน voted ปุ่มทั้งสองจะปลดล็อก ``` การตรวจสอบนี้เกิดขึ้นก่อนที่ผู้ลงคะแนนจะแตะต้องอะไร หากพวกเขาลงคะแนนไปแล้ว พวกเขาจะเห็นทันที ไม่มีกรณี "ลงคะแนนแล้วพบว่าถูกบล็อก" — สัญลักษณ์ล็อกจะปรากฏให้เห็นตั้งแต่โหลดหน้าเว็บ 5.2 การลงคะแนน ``` 1. ผู้ลงคะแนนแตะ YES หรือ NO บนการ์ดบุคคล 2. ปุ่มทั้งสองบนการ์ดนั้นจะถูกปิดใช้งานทันที (UI แบบมองโลกในแง่ดี) 3. ส่งข้อมูล POST / ด้วยเนื้อหา JSON: { question_id, answer, screen, lat, lon, accuracy, location, user_id } หมายเหตุ: ไม่มีการส่งลายเซ็น เซิร์ฟเวอร์จะคำนวณเอง 4. เซิร์ฟเวอร์: · อ่าน REMOTE_ADDR และ HTTP_USER_AGENT จากคำขอ · คำนวณลายเซ็นใหม่ = SHA256(ip | ua | screen | lat₃ | lon₃) · INSERT INTO votes (…) VALUES (…) 5. หาก INSERT สำเร็จ: → 200 { ok:true, yes, no, signature } หากมีการละเมิด UNIQUE: → 409 { ok:false, error:"duplicate_signature", yes, no } 6. ไคลเอนต์อัปเดต: · .p-counts ด้วยผลรวม yes/no ใหม่ · markVoted(person, answer) → ล็อกปุ่มทั้งสองปุ่มแสดงข้อความ ✔ · localStorage f1votes[qid] = คำตอบ (ทางลัดสำหรับการโหลดครั้งถัดไป) · แถบผลรวมทั่วโลก (ผลรวมของจำนวนนับต่อคน) ``` 5.3 การรับประกัน · ความเป็นอะตอมิก — การแทรกจะสำเร็จหรือล้มเหลวเท่านั้น ไม่มีการเขียนบางส่วน · ความไม่เปลี่ยนแปลงของการแสดงผล — ไม่ว่าการโหวตจะสำเร็จใหม่หรือเป็นการซ้ำที่ถูกปฏิเสธ ไคลเอนต์จะแสดงสถานะการล็อกและผลรวมที่ถูกต้อง · การนับที่ได้รับอนุญาตจากเซิร์ฟเวอร์ — จำนวนใช่/ไม่ใช่ที่ส่งคืนในคำตอบจะถูกคำนวณใหม่โดยเซิร์ฟเวอร์จากตารางโหวต ไม่ได้มาจากอินพุตของไคลเอนต์ --- 6. โมเดลภัยคุกคาม การลดภัยคุกคาม การล้างแคช / คุกกี้ / ประวัติเพื่อโหวตใหม่ การบังคับใช้จะอยู่ใน SQLite ไม่ใช่ในเบราว์เซอร์ รอยเท้าเดียวกัน → ลายเซ็นเดียวกัน → 409 การเปลี่ยนไปใช้โหมดส่วนตัว/ไม่ระบุตัวตน โหมดไม่ระบุตัวตนจะไม่เปลี่ยน IP, UA หรือ GPS ลายเซ็นไม่เปลี่ยนแปลง ถูกบล็อก การติดตั้งเบราว์เซอร์ใหม่ IP/UA/GPS เดียวกัน → ลายเซ็นเดียวกัน ถูกบล็อก การใช้โทรศัพท์เครื่องอื่นบน Wi-Fi เดียวกัน UA และหน้าจอต่างกัน → ลายเซ็นต่างกัน อนุญาต (นี่คือการออกแบบ การสำรวจความคิดเห็นเป็นแบบต่ออุปกรณ์ ไม่ใช่ต่อครัวเรือน) การปลอมแปลง GPS ผ่านเครื่องมือสำหรับนักพัฒนา การปลอมแปลง GPS ใดๆ จะสร้างลายเซ็นที่ต้องตรงกับลายเซ็นที่จัดเก็บไว้จึงจะถูกบล็อก การปลอมแปลงใหม่จะสร้างลายเซ็นใหม่และการลงคะแนนใหม่ ตรวจพบเฉพาะเมื่อผู้ดูแลระบบสังเกตแผนที่ การแก้ไข: ผู้ดูแลระบบสามารถตรวจสอบคอลัมน์ลายเซ็น IP และตำแหน่งใน admin.php ได้ การโจมตีแบบ Man-in-the-middle ยุติ TLS ที่พร็อกซีแบบย้อนกลับ ตั้งค่า HSTS API รับเฉพาะ JSON POST เท่านั้น ไม่มีคุกกี้ที่เก็บสถานะการลงคะแนน การโจมตีแบบ Brute-force รหัสผ่านผู้ดูแลระบบ ล็อกเอาต์ 5 ครั้งพร้อมคูลดาวน์ 60 วินาที การเปรียบเทียบแบบคงที่ hash_equals หน่วงเวลาเทียม 400 มิลลิวินาทีเมื่อล้มเหลว การโจมตีแบบ SQL injection การสืบค้นทั้งหมดใช้คำสั่งเตรียมการ PDO พร้อมพารามิเตอร์ที่ผูกไว้ XSS ในข้อความคำถามหรือการแสดงรอยเท้า เอาต์พุตทั้งหมดผ่าน htmlspecialchars($s, ENT_QUOTES) การลงคะแนนซ้ำด้วยลายเซ็นที่ถูกขโมย ลายเซ็นไม่ใช่ความลับ มันเป็นตัวระบุสาธารณะของรอยเท้า ผู้โจมตีที่รู้ลายเซ็นก็ยังไม่สามารถแทรกสำเนาได้เพราะข้อจำกัดของฐานข้อมูลปฏิเสธ วิธีเดียวที่จะลงคะแนนได้คือต้องเป็นรอยเท้าเอง การหมุนเวียน IP ของผู้ให้บริการระหว่างเซสชัน หากผู้ให้บริการหมุนเวียน IP ระหว่างการตรวจสอบเบื้องต้นและการส่ง POST ลงคะแนน ลายเซ็นจะแตกต่างกันและการลงคะแนนจะสำเร็จแม้ว่าการตรวจสอบเบื้องต้นจะบอกว่า "ลงคะแนนไปแล้ว" การแก้ไข: การตรวจสอบเบื้องต้นเป็นเพียงคำแนะนำ ข้อจำกัดของฐานข้อมูลเป็นตัวตัดสินขั้นสุดท้าย หน้าต่างมีขนาดเล็ก (โดยทั่วไปคือการโหลดหน้าเว็บหนึ่งหน้า) 6.1 การแก้ไขที่ทราบแต่ไม่สามารถแก้ไขได้ · การสลับ VPN ผู้ลงคะแนนสามารถหมุนเวียน IP และลงคะแนนได้หลายครั้ง โดยแต่ละครั้งมาจากโหนดทางออกที่แตกต่างกัน การแก้ไขปัญหานี้จะต้องใช้ผู้ให้บริการข้อมูลประจำตัวที่เชื่อถือได้ ซึ่งอยู่นอกขอบเขตสำหรับการสำรวจความคิดเห็นแบบไม่ต้องเข้าสู่ระบบและไม่ต้องสร้างบัญชี • อุปกรณ์ที่ใช้ร่วมกัน ครัวเรือนที่มีโทรศัพท์เพียงเครื่องเดียวไม่สามารถให้สิทธิ์ออกเสียงแก่สมาชิกทุกคนได้ นี่เป็นการแลกเปลี่ยนที่ตั้งใจไว้ • ความคลาดเคลื่อนของสัญญาณ GPS บริเวณขอบเขตการปัดเศษ โทรศัพท์ที่อยู่ตรงกลางระหว่างช่องสี่เหลี่ยม 3 ตำแหน่ง อาจให้สัญญาณที่แตกต่างกันได้ในบางครั้ง ในทางปฏิบัติแล้วเหตุการณ์นี้เกิดขึ้นได้ยาก ความคลาดเคลื่อนจากการปัดเศษมีขอบเขตประมาณ 55 เมตรต่ำกว่าค่าความคลาดเคลื่อนของโทรศัพท์ที่ตั้งอยู่กับที่ในอาคารมาก --- 7. การแปลงพิกัด GPS เป็นชื่อทางภูมิศาสตร์ เมื่อล็อกพิกัด GPS ได้แล้ว ไคลเอนต์จะเรียกใช้คำสั่ง: ``` GET https://nominatim.openstreetmap.org/reverse?format=jsonv2 &lat=…&lon=…&zoom=16&addressdetails=1 ``` เซิร์ฟเวอร์จะไม่เรียกใช้คำสั่งใดๆ ไคลเอนต์จะเป็นผู้เรียกใช้เอง ชื่อถนนที่ได้ (ถนน ทางเท้า ย่าน ชานเมือง หมู่บ้าน เมือง หรือนคร ตามลำดับความสำคัญ) จะถูกเก็บไว้ใน votes.location และแสดงบนส่วนหัวของการ์ด ในฟีด และในป๊อปอัพของเครื่องหมายบนแผนที่แต่ละอัน นโยบายการใช้งานสาธารณะของ Nominatim กำหนดให้ต้องมี User-Agent ที่ถูกต้องและอัตราการร้องขอต่ำกว่า 1 ครั้งต่อวินาที หน้าเว็บจะเรียกใช้เพียงครั้งเดียวต่อการล็อกพิกัด GPS ดังนั้นจึงเป็นไปตามข้อกำหนดนี้ --- 8. การแสดงผลแผนที่ แผนที่นี้ให้บริการโดย Leaflet 1.9.4 (CDN) และไทล์ OpenStreetMap การใช้งานพฤติกรรม มุมมองสะสม ?action=map_points ส่งคืนคะแนนโหวตทั้งหมดที่มีละติจูด/ลองจิจูดที่ไม่เป็นค่าว่าง รหัสสี วงกลมสีเขียวสำหรับใช่ วงกลมสีแดงสำหรับไม่ใช่ รัศมี 7 พิกเซลสำหรับจุดข้อมูลในอดีต 10 พิกเซลสำหรับการแตะ GPS ครั้งเดียวจากการ์ดสด ป๊อปอัพ ชื่อบุคคล คำตอบ เวลา ตำแหน่งที่แปลงกลับเป็นพิกัดทางภูมิศาสตร์ คำนำหน้าลายเซ็น (16 ตัวอักษร) การซูม fitBounds บนทุกจุดที่มี maxZoom:16 จุดศูนย์กลางของจุดเดียวอยู่ที่การซูม 16 จุดศูนย์กลางของการแตะ GPS อยู่ที่การซูม 17 คำอธิบายสัญลักษณ์ คงที่ด้านล่างซ้าย แสดงคำอธิบายสีเขียว/แดง โอเวอร์เลย์แผนที่อยู่ในตำแหน่งคงที่และใช้พื้นที่ 84 vh (สูงสุด 640 พิกเซล) พร้อมปุ่ม "ปิด" ที่ชัดเจน การแตะที่ขอบสีเข้มจะปิดโอเวอร์เลย์ด้วย ไม่จำเป็นต้องใช้แป้นพิมพ์ --- 9. ฟีดบุคคล ทุกคำถามคือบุคคล หน้าเว็บสาธารณะแสดงผลตามลำดับรหัส โดยแต่ละรหัสจะแสดงเป็นบัตรบุคคล: ``` ┌───────────────────────────────────────────┐ │ Evan Winter │ │ การเจรจาและโลจิสติกส์ในอเมริกากลาง… │ │ @2026-09-28 15:28:18 · ถนน 15 de Febrero │ │ │ │ ┌──────────────┐ ┌───────────────┐ │ │ │ ใช่ │ │ ไม่ใช่ │ │ │ └──────────────┘ └──────────────┘ │ │ ✔ รอยเท้าของคุณโหวต YES แล้ว │ │ │ │ Y 2 N 0 2 รวม │ │ ─────────────────────────────────────── │ │ แสดงรอยเท้า ▾ │ └────────────────────────────────────────────┘ ``` การแตะที่ชื่อหรือปุ่มสลับจะขยายการ์ดเพื่อแสดงแต่ละคะแนนโหวตเป็นบล็อกข้อมูลแบบเต็ม: ตราสัญลักษณ์, เวลา, สถานที่, IP, อุปกรณ์, หน้าจอ, GPS และคำนำหน้าลายเซ็น การแตะที่ค่า GPS จะเปิดแผนที่ที่โฟกัสไปที่พิกัดนั้น รายชื่อผู้ติดต่อ 53 คนประกอบด้วยรายชื่อผู้ติดต่อของ Fiveo1 รวมถึงเจ้าหน้าที่การเลือกตั้งทั่วไปของรัฐแคลิฟอร์เนียในวันที่ 3 พฤศจิกายน 2026 ที่ระบุไว้ในเอกสารที่ให้มา (ซานดิเอโก, ริเวอร์ไซด์, ซานเบอร์นาร์ดิโน, โคโรนา, แอปเปิลวัลเลย์, วิกเตอร์วิลล์) --- 10. การบริหารจัดการ admin.php ได้รับการป้องกันด้วยรหัสผ่านเดียว (@Evan.Winter ในเวอร์ชันที่จัดส่ง) พร้อมการควบคุมดังต่อไปนี้: · เพิ่มคำถาม / บุคคล รูปแบบ: ชื่อ — บทบาท · ตำแหน่ง · เมือง · ดูคำถามทั้งหมดพร้อมจำนวนคำตอบใช่/ไม่ใช่ และจำนวนลายเซ็นที่ไม่ซ้ำกันต่อคน• ลบคำถามเดียวและคะแนนโหวต (แบบธุรกรรม) • ลบคะแนนโหวตทั้งหมดในทุกคำถาม (เก็บคำถามไว้) • ลายเซ็นล่าสุด 40 รายการ พร้อม IP, ตำแหน่ง, เวลา และคำนำหน้าลายเซ็น • ออกจากระบบ หน้าผู้ดูแลระบบไม่ได้เชื่อมโยงจากหน้าสาธารณะ ต้องเข้าถึงโดยการพิมพ์ URL เท่านั้น ส่วนท้ายของหน้าสาธารณะจะแสดงเฉพาะ: ``` Fiveo1 PO Box 312786 Avalon, California 90704 Evan Winter ``` --- 11. ข้อกำหนดในการใช้งาน ข้อกำหนด ค่า PHP 7.4 หรือใหม่กว่า ส่วนขยาย pdo_sqlite, json, session ระบบไฟล์ สิทธิ์ในการเขียนบนไดเร็กทอรีที่มี index.php (สำหรับ fiveo1.sqlite และ WAL/SHM ที่เกี่ยวข้อง) เว็บเซิร์ฟเวอร์ Apache หรือ Nginx พร้อมการยุติ TLS ไฟล์ index.php, admin.php ฐานข้อมูล สร้างโดยอัตโนมัติเมื่อมีการร้องขอครั้งแรก ขาออก เฉพาะเบราว์เซอร์ของผู้ลงคะแนนเท่านั้นที่ติดต่อ Nominatim และไทล์ OSM เซิร์ฟเวอร์ไม่ทำการโทรออก 11.1 รายการตรวจสอบการรักษาความปลอดภัย ☐ บังคับใช้ HTTPS; เปิดใช้งาน HSTS ☐ ตั้งค่า session.cookie_secure=1, session.cookie_httponly=1, session.cookie_samesite=Lax ☐ เปลี่ยน ADMIN_PASSWORD ใน admin.php ☐ ปิดใช้งานการแสดงรายการไดเร็กทอรี ☐ เพิ่ม robots.txt disallow สำหรับ admin.php ☐ สำรองข้อมูล fiveo1.sqlite (และไฟล์ที่เกี่ยวข้อง -wal / -shm) ตามกำหนดเวลา ☐ ตั้งค่า upload_max_filesize ให้ต่ำและปิดใช้งาน allow_url_fopen หากไม่จำเป็น ☐ จำกัดสิทธิ์การเขียนไปยังไดเร็กทอรี SQLite ให้กับผู้ใช้ PHP-FPM เท่านั้น --- 12. หมายเหตุการใช้งาน · เปิดใช้งานโหมด WAL ดังนั้นการอ่านจะไม่บล็อกการเขียน ผู้อ่านจะเห็นภาพรวมที่สอดคล้องกันแม้ในขณะที่กำลังแทรกคะแนนโหวต · ลายเซ็นจะถูกจัดเก็บในรูปแบบข้อความธรรมดาใน votes.signature นี่คือค่าแฮช SHA-256 ไม่ใช่ข้อมูลลับ การจัดเก็บแบบเปิดเผยช่วยให้การบังคับใช้ UNIQUE มีประสิทธิภาพและค้นหา my_signature ได้อย่างรวดเร็ว · UA จะถูกตัดเหลือ 400 ตัวอักษรสำหรับการจัดเก็บเท่านั้น UA แบบเต็มจะใช้สำหรับการแฮช · คอลัมน์ user_id เก็บ UUID 16 ตัวอักษรฐานสิบหกที่สร้างขึ้นครั้งเดียวต่อเบราว์เซอร์ผ่าน crypto.getRandomValues ​​และเก็บไว้ใน localStorage ใช้สำหรับบันทึกที่อ่านง่ายสำหรับมนุษย์และการปรับปรุงในอนาคตเท่านั้น ไม่ใช่ส่วนหนึ่งของลายเซ็น และการล้างค่าจะไม่ปลดล็อกการโหวต · เขตเวลาใน timestamp ทั้งหมดคือเวลาท้องถิ่นของเซิร์ฟเวอร์ หากต้องการเขตเวลามาตรฐานเดียว ให้ตั้งค่าที่ด้านบนของ index.php ด้วย date_default_timezone_set('America/Los_Angeles') --- 13. ข้อจำกัดและงานในอนาคต ข้อจำกัด ทิศทางในอนาคตที่เป็นไปได้ การหมุนเวียน VPN จะทำให้การผูก IP ล้มเหลว การยืนยันทางอีเมลหรือ SMS เป็นทางเลือก ครัวเรือนใช้โทรศัพท์เครื่องเดียว รหัสผ่านต่อผู้ลงคะแนนที่ออกโดยผู้ดูแลระบบเป็นทางเลือก การปลอมแปลง GPS ตรวจจับไม่ได้ การตรวจสอบความถูกต้องฝั่งเซิร์ฟเวอร์; ทำเครื่องหมายพิกัดที่ไม่น่าเชื่อถือ ไม่มีบันทึกการตรวจสอบสำหรับการกระทำของผู้ดูแลระบบ ตาราง admin_log แบบเพิ่มข้อมูลเท่านั้น ไม่มีการจำกัดอัตราบนจุดสิ้นสุดการลงคะแนน ถังโทเค็นต่อ IP ก่อนการแทรก Nominatim จำกัดอัตรา Nominatim ที่โฮสต์เองหรือ geocoder แบบชำระเงิน ข้อจำกัดการเขียนเดียวของ SQLite ย้ายไปใช้ PostgreSQL หากการทำงานพร้อมกันเกิน ~100 การเขียน/วินาที ไม่มีการส่งออก การส่งออก CSV ของผู้ดูแลระบบของตารางคะแนนทั้งหมด --- 14. สรุป แบบสำรวจการลงคะแนนที่ปลอดภัยของ Fiveo1 บรรลุคุณสมบัติที่เข้มงวด — การลงคะแนนใช่/ไม่ใช่หนึ่งครั้งต่อพื้นที่ ต่อคน ตลอดไป — โดยใช้ไฟล์ PHP เพียงไฟล์เดียวไฟล์ SQLite ไฟล์เดียว และแฮช SHA-256 ฝั่งเซิร์ฟเวอร์ของ IP, User-Agent, รูปทรงหน้าจอ และ GPS ที่ปัดเศษเป็น ~110 เมตร การบังคับใช้ไม่ได้อยู่ใน JavaScript ไม่ได้อยู่ในคุกกี้ และไม่ได้ขึ้นอยู่กับความสมัครใจของลูกค้า: มันคือข้อจำกัด UNIQUE ที่บังคับใช้โดยเอนจิน SQLite เอง ทุกการลงคะแนนจะสร้างรายการวิเคราะห์ทางนิติวิทยาศาสตร์แบบเต็มรูปแบบ (IP, อุปกรณ์, หน้าจอ, GPS, ตำแหน่ง, เวลา, แฮชลายเซ็น) และทุกรายการจะถูกพล็อตสะสมบน OpenStreetMap เพื่อให้ผู้ดูแลระบบสามารถดูได้อย่างแม่นยำว่าคำตอบแต่ละข้อถูกลงคะแนนที่ใดและเมื่อใด ระบบนี้ใช้งานได้เฉพาะบนมือถือ สัมผัสเท่านั้น ไม่ต้องใช้แป้นพิมพ์ และไม่ต้องล็อกอิน และได้รับการออกแบบให้ใช้งานเป็นไฟล์คู่ในไดเร็กทอรี public_html ใดก็ได้ที่มี PHP และ pdo_sqlite --- สิ้นสุดเอกสารไวท์เปเปอร์ — Fiveo1 Secure Voting Poll v1.0 ← กลับไปที่ Fiveo1
← Back to Fiveo1
▼
▲