การเข้ารหัส IoT ในทางปฏิบัติบนเอสเพรสซิฟ ESP8266

ชิปเซ็ตเอสเพรสซิฟ ESP8266 ทำให้กระดานพัฒนา ‘อินเทอร์เน็ตของสิ่งต่าง ๆ ‘ สามดอลลาร์เป็นความจริงทางเศรษฐกิจ ตามเว็บไซต์สร้างเฟิร์มแวร์อัตโนมัติยอดนิยมในสัปดาห์ที่ผ่านมา 60 วันมีการสร้างเฟิร์มแวร์ที่กำหนดเอง 13,341 ชิ้นสำหรับแพลตฟอร์มนั้น ของผู้ที่มีเพียง 19% มีการสนับสนุน SSL และ 10% รวมถึงโมดูลการเข้ารหัส

เรามักจะมีความสำคัญต่อการขาดความปลอดภัยในภาค IOT และมักจะครอบคลุม botnets และการโจมตีอื่น ๆ แต่เราจะถือโครงการของเราให้เป็นมาตรฐานเดียวกันกับที่เราต้องการหรือไม่? เราจะหยุดที่ระบุปัญหาหรือเราสามารถเป็นส่วนหนึ่งของการแก้ปัญหาได้หรือไม่?

บทความนี้จะมุ่งเน้นไปที่การใช้ฟังก์ชันการให้สิทธิ์การเข้ารหัส AES และแฮชไปยังโปรโตคอล MQTT โดยใช้ชิป ESP8266 ยอดนิยมที่ใช้เฟิร์มแวร์ NodeMCU จุดประสงค์ของเราคือไม่ให้การคัดลอก / วางยาครอบจักรวาล แต่ต้องผ่านกระบวนการทีละขั้นตอนระบุความท้าทายและแนวทางแก้ไขไปพร้อมกัน ผลที่ได้คือระบบที่มีการเข้ารหัสและการรับรองความถูกต้องแบบสิ้นท้ายและการป้องกันการดักฟังไปพร้อมกันและการปลอมแปลงข้อมูลที่ถูกต้องโดยไม่ต้องพึ่งพา SSL

เราตระหนักว่านอกจากนี้ยังมีแพลตฟอร์มที่มีประสิทธิภาพมากขึ้นที่สามารถรองรับ SSL ได้อย่างง่ายดาย (เช่น Raspberry Pi, Orange Pi, Friendlyarm) แต่เริ่มต้นด้วยฮาร์ดแวร์ที่ถูกที่สุดของเราส่วนใหญ่นอนอยู่รอบ ๆ และโปรโตคอลที่เหมาะสำหรับหลายโครงการของเรา . AES เป็นสิ่งที่คุณสามารถใช้งานได้ใน AVR หากคุณต้องการ

ทฤษฎี

MQTT เป็นโปรโตคอลการส่งข้อความที่มีน้ำหนักเบาที่ทำงานอยู่ด้านบนของ TCP / IP และใช้งานบ่อยสำหรับโครงการ IoT อุปกรณ์ไคลเอนต์สมัครสมาชิกหรือเผยแพร่เป็นหัวข้อ (E.G. เซ็นเซอร์ / อุณหภูมิ / ห้องครัว) และข้อความเหล่านี้จะถูกส่งโดยนายหน้าซื้อขาย MQTT ข้อมูลเพิ่มเติมเกี่ยวกับ MQTT มีอยู่ในหน้าเว็บของพวกเขาหรือในซีรีส์เริ่มต้นของเราเอง

โปรโตคอล MQTT ไม่มีคุณสมบัติความปลอดภัยในตัวนอกเหนือจากการรับรองความถูกต้องชื่อผู้ใช้ / รหัสผ่านดังนั้นจึงเป็นเรื่องปกติที่จะเข้ารหัสและรับรองความถูกต้องในเครือข่ายที่มี SSL อย่างไรก็ตาม SSL สามารถเรียกร้องได้ค่อนข้างสำหรับ ESP8266 และเมื่อเปิดใช้งานคุณจะเหลือหน่วยความจำน้อยลงสำหรับแอปพลิเคชันของคุณ ในฐานะที่เป็นทางเลือกที่มีน้ำหนักเบาคุณสามารถเข้ารหัสเฉพาะข้อมูลการจ่ายเงินที่ถูกส่งและใช้ ID เซสชันและฟังก์ชั่นแฮชสำหรับการรับรองความถูกต้อง

วิธีที่ตรงไปตรงมาในการทำเช่นนี้คือการใช้ Lua และ NodeMcu Crypto Module ซึ่งรวมถึงการสนับสนุนอัลกอริทึม AES ในโหมด CBC เช่นเดียวกับฟังก์ชั่นแฮช HMAC การใช้การเข้ารหัส AES อย่างถูกต้องต้องใช้สามสิ่งในการผลิต ciphertext: ข้อความสำคัญและเวกเตอร์การเริ่มต้น (IV) ข้อความและคีย์เป็นแนวคิดที่ตรงไปตรงมา แต่เวกเตอร์การเริ่มต้นมีค่าการสนทนาบางอย่าง

เมื่อคุณเข้ารหัสข้อความใน AES ด้วยปุ่มสแตติกมันจะสร้างเอาต์พุตเดียวกันเสมอ ตัวอย่างเช่นข้อความ “USERNAMEPASSWORD” เข้ารหัสด้วยคีย์ “1234567890abcdef” อาจสร้างผลลัพธ์เช่น “E40D86C04D723Aff” หากคุณเรียกใช้การเข้ารหัสอีกครั้งด้วยคีย์และข้อความเดียวกันคุณจะได้รับผลลัพธ์เดียวกัน สิ่งนี้เปิดให้คุณเข้าถึงการโจมตีทั่วไปหลายประเภทโดยเฉพาะการวิเคราะห์รูปแบบและการโจมตีซ้ำ

ในการโจมตีการวิเคราะห์รูปแบบคุณใช้ความรู้ที่ชิ้นส่วนของข้อมูลที่กำหนดจะสร้าง ciphertext เดียวกันเสมอเพื่อคาดเดาว่าวัตถุประสงค์หรือเนื้อหาของข้อความที่แตกต่างกันโดยไม่ต้องรู้รหัสลับจริง ๆ ตัวอย่างเช่นหากข้อความ “E40D86C04D723Aff” จะถูกส่งก่อนการสื่อสารอื่น ๆ ทั้งหมดหนึ่งอาจคาดเดาได้อย่างรวดเร็วว่าเป็นการเข้าสู่ระบบ ในระยะสั้นหากระบบเข้าสู่ระบบเป็นเรื่องง่ายส่งแพ็กเก็ตนั้น (การโจมตีเล่นซ้ำ) อาจเพียงพอที่จะระบุตัวเองในฐานะผู้ใช้ที่ได้รับอนุญาตและความโกลาหลตามมา

IVs ทำให้การวิเคราะห์รูปแบบยากขึ้น IV เป็นข้อมูลที่ส่งไปพร้อมกับคีย์ที่ปรับเปลี่ยนผลลัพธ์สิ้นสุด ciphertext ตามชื่อที่แนะนำมันจะเริ่มต้นสถานะของอัลกอริทึมการเข้ารหัสก่อนที่ข้อมูลจะเข้าสู่ IV จะต้องแตกต่างกันสำหรับแต่ละข้อความที่ส่งเพื่อให้ข้อมูลซ้ำ ๆ เข้ารหัสเป็น ciphertext ที่แตกต่างกันและ ciphers บางอย่าง (เช่น AES-CBC) ต้องการที่จะคาดเดาไม่ได้ – วิธีปฏิบัติในการทำสิ่งนี้เป็นเพียงการสุ่มมันในแต่ละครั้ง IVs ไม่จำเป็นต้องเก็บรักษาความลับ แต่เป็นเรื่องปกติที่จะทำให้งงในทางใดทางหนึ่ง

ในขณะที่สิ่งนี้ปกป้องการวิเคราะห์รูปแบบ แต่ก็ไม่ได้ช่วยในการโจมตีซ้ำ ตัวอย่างเช่นการส่งสัญญาณข้อมูลที่ได้รับการเข้ารหัสที่กำหนดจะยังคงทำซ้ำผลลัพธ์ เพื่อป้องกันไม่ให้เราจำเป็นต้องรับรองความถูกต้องของผู้ส่ง เราจะใช้รหัสเซสชั่นที่สร้างขึ้นของสาธารณะสำหรับแต่ละข้อความ ID เซสชันนี้สามารถสร้างได้โดยอุปกรณ์รับโดยโพสต์ไปยังหัวข้อ MQTT

การป้องกันการโจมตีประเภทนี้มีความสำคัญในกรณีการใช้งานทั่วไปสองครั้ง เตาอินเทอร์เน็ตที่ควบคุมได้มีอยู่และยูทิลิตี้ที่น่าสงสัยนอกเหนือจากนั้นจะดีถ้าพวกเขาไม่ได้ใช้คำสั่งที่ไม่ปลอดภัย ประการที่สองถ้าฉันมีคะแนนจากเซ็นเซอร์หนึ่งร้อยตัวฉันไม่ต้องการให้ใครกรอกฐานข้อมูลของฉันด้วยขยะ

การเข้ารหัสในทางปฏิบัติ

การใช้งานข้างต้นใน NodeMCU ต้องใช้ความพยายามบางอย่าง คุณจะต้องมีการรวบรวมเฟิร์มแวร์เพื่อรวมโมดูล ‘crypto’ นอกเหนือจากคนอื่น ๆ ที่คุณขอire สำหรับใบสมัครของคุณ ไม่จำเป็นต้องมีการรองรับ SSL

ก่อนอื่นเรามาสมมติว่าคุณเชื่อมต่อกับนายหน้าซื้อขาย MQTT ด้วยสิ่งดังต่อไปนี้ คุณสามารถใช้งานนี้เป็นฟังก์ชั่นแยกต่างหากจากการเข้ารหัสเพื่อให้สิ่งต่าง ๆ สะอาด ไคลเอนต์สมัครสมาชิกช่อง SessionID ซึ่งเผยแพร่ ID เซสชัน Pseudorandom ที่เหมาะสม คุณสามารถเข้ารหัสได้ แต่ไม่จำเป็น

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
m = mqtt.client (& quot; clientid & quot;, 120)

m: เชื่อมต่อ (& quot; myserver.com & quot;, 1883, 0,
ฟังก์ชั่น (ไคลเอนต์)
พิมพ์ (& quot; เชื่อมต่อและ quot;)
ลูกค้า: สมัครสมาชิก (& quot; mytopic / sessionid & quot;, 0,
ฟังก์ชั่น (ไคลเอนต์) พิมพ์ (& quot; สมัครสมาชิกสำเร็จ & quot;) สิ้นสุด
)
จบ,
ฟังก์ชั่น (ไคลเอนต์, เหตุผล)
พิมพ์ (& quot; เหตุผลที่ล้มเหลว: & quot; .. เหตุผล)
จบ
)

m: บน (& quot; ข้อความ & quot;, ฟังก์ชั่น (ไคลเอนต์, หัวข้อ, sessionid) สิ้นสุด)

การเคลื่อนไหวของโหนด ID เป็นวิธีที่สะดวกในการช่วยระบุแหล่งข้อมูล คุณสามารถใช้สตริงใด ๆ ที่คุณต้องการ: nodeid = node.chipid ()

จากนั้นเราตั้งค่าเวกเตอร์การเริ่มต้นแบบคงที่และกุญแจ นี่เป็นเพียงการใช้ในการทำให้เวกเตอร์การเริ่มต้นแบบสุ่มที่ส่งมาพร้อมกับแต่ละข้อความไม่ได้ใช้สำหรับข้อมูลใด ๆ นอกจากนี้เรายังเลือกคีย์แยกต่างหากสำหรับข้อมูล ปุ่มเหล่านี้เป็นเลขฐานสิบหก 16 บิตเพียงแทนที่ด้วยของคุณ

ในที่สุดเราจะต้องมีวลีรหัสผ่านสำหรับฟังก์ชั่นแฮชที่เราจะใช้ในภายหลัง สตริงของความยาวที่เหมาะสมนั้นดี

1
2
3
4
staticiv = & quot; abcdef2345678901 & quot;
ivkey = & quot; 2345678901abcdef & quot;
Datakey = & quot; 0123456789abcdef & quot;
POSTPRASE = & quot; mypassphrase & quot;

เราจะถือว่าคุณมีแหล่งข้อมูลบางอย่าง สำหรับตัวอย่างนี้มันจะเป็นการอ่านค่าจาก ADC ข้อมูล = adc.read (0)

ตอนนี้เราสร้างเวกเตอร์การเริ่มต้น pseudorandom เลขฐานสิบหก 16 หลักมีขนาดใหญ่เกินไปสำหรับฟังก์ชั่นหมายเลข Pseudorandom ดังนั้นเราจึงสร้างมันในสองครึ่ง (16 ^ 8 ลบ 1) และต่อกัน

1
2
3
4
5
half1 = node.random (4294967295)
half2 = node.random (4294967295)
i = string.format (& quot;% 8x & quot; half1)
v = string.format (& quot;% 8x & quot; half2)
iv = ฉัน .. v

ตอนนี้เราสามารถเรียกใช้การเข้ารหัสจริง ที่นี่เรากำลังเข้ารหัสเวกเตอร์การเริ่มต้นปัจจุบันรหัสโหนดและข้อมูลเซ็นเซอร์หนึ่งชิ้น

1
2
3
Encrypted_iv = crypto.encrypt (& quot; aes-cbc & quot;, ivkey, iv, staticiv)
Encrypted_nodeid = crypto.encrypt (& quot; aes-cbc & quot;, datakey, nodeid, iv)
Encrypted_data = crypto.encrypt (& quot; aes-cbc & quot;, datakey, data, iv)

ตอนนี้เราใช้ฟังก์ชั่นแฮชสำหรับการรับรองความถูกต้อง ครั้งแรกที่เรารวมรหัส NODEID, IV, และเซสชันเซสชันลงในข้อความเดียวจากนั้นคำนวณแฮช HMAC SHA1 โดยใช้รหัสผ่านที่เรากำหนดไว้ก่อนหน้านี้ เราแปลงเป็นเลขฐานสิบหกเพื่อให้มนุษย์สามารถอ่านได้มากขึ้นสำหรับการดีบักใด ๆ

1
2
FullMessage = NODEID .. IV .. ข้อมูล .. SessionID
hmac = crypto.tohex (crypto.hmac (& quot; sha1 & quot; ,, fullmessage, รหัสผ่าน))

ขณะนี้มีการตรวจสอบการเข้ารหัสและการตรวจสอบความถูกต้องแล้วเราสามารถวางข้อมูลทั้งหมดนี้ในบางโครงสร้างและส่งได้ ที่นี่เราจะใช้เครื่องหมายจุลภาคที่แยกจากกันตามที่สะดวก:

1
2
payload = table.concat ({encrypted_iv, eid, data1, hmac}, & quot; & quot;)
M: เผยแพร่ (& quot; yourmqtttopic & quot;, payload, 2, 1, ฟังก์ชั่น (ไคลเอนต์) p = & quot; ส่ง & quot; พิมพ์ (p) end)

เมื่อเรารันรหัสข้างต้นใน NodeMCU ที่แท้จริงเราจะได้รับผลลัพธ์แบบนี้:

1D54DD1AF0F75A91A00D4DCD8F4AD28D,
D1A0B14D187C5ADFC948DFD77C2B2E5,
564633A4A053153BCBD6ED25370346D5,
C66697DF7E7D467112757C841BFB6BCE051D6289

ทั้งหมดเข้าด้วยกันโปรแกรมการเข้ารหัสมีดังนี้ (แยกส่วน MQTT เพื่อความชัดเจน):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
nodeid = node.chipid ()
staticiv = & quot; abcdef2345678901 & quot;
ivkey = & quot; 2345678901abcdef & quot;
Datakey = & quot; 0123456789abcdef & quot;
POSTPRASE = & quot; mypassphrase & quot;

ข้อมูล = adc.read (0)
half1 = node.random (4294967295)
half2 = node.random (4294967295)
i = string.format (& quot;% 8x & quot; half1)
v = string.format (& quot;% 8x & quot; half2)
iv = ฉัน .. v

Encrypted_iv = crypto.encrypt (& quot; aes-cbc & quot;, ivkey, iv, staticiv)
Encrypted_nodeid = crypto.encrypt (& quot; aes-cbc & quot;, datakey, nodeid, iv)
Encrypted_data = crypto.encrypt (& quot; aes-cbc & quot;, datakey, data, iv)
FullMessage = NODEID .. IV .. ข้อมูล .. SessionID
hmac = crypto.toHex (crypto.hmac (& quot; sha1 & quot;, fullmessage, รหัสผ่าน))
payload = table.concat ({encrypted_iv, Encrypted_Nodeid, Encrypted_Data, HMAC}, & quot; & quot;)

การถอดรหัส

ตอนนี้นายหน้า MQTT ของคุณไม่รู้หรือใส่ใจว่าข้อมูลถูกเข้ารหัสมันก็ผ่านไปได้ ดังนั้นลูกค้า MQTT อื่น ๆ ของคุณที่สมัครรับหัวข้อจะต้องรู้วิธีถอดรหัสข้อมูล ที่ NodeMcu นี่ค่อนข้างง่าย เพียงแยกข้อมูลที่ได้รับเป็นสตริงผ่านเครื่องหมายจุลภาคและทำอะไรบางอย่างเช่นด้านล่าง หมายเหตุจุดสิ้นสุดนี้จะสร้าง ID เซสชันดังนั้นรู้อยู่แล้ว

1
2
3
4
5
6
7
8
9
10
staticiv = & quot; abcdef2345678901 & quot;
ivkey = & quot; 2345678901abcdef & quot;
Datakey = & quot; 0123456789abcdef & quot;
POSTPRASE = & quot; mypassphrase & quot;

IV = crypto.decrypt (& quot; aes-cbc & quot;, ivkey, Encrypted_iv, Staticiv)
nodeid = crypto.decrypt (& quot; aes-cbc & quot;, Datakey, Encrypted_Nodeid, IV)
ข้อมูล = crypto.decrypt (& quot; aes-cbc & quot;, datakey, Encrypted_Data, IV)
FullMessage = NODEID .. IV .. ข้อมูล .. SessionID
hmac = crypto.toHex (crypto.hmac (& quot; sha1 & quot;, fullmessage, รหัสผ่าน))

จากนั้นเปรียบเทียบ HMAC ที่ได้รับและคำนวณได้และไม่คำนึงถึงผลลัพธ์ทำให้ ID เซสชันนั้นเป็นโมฆะโดยสร้างใหม่

อีกครั้งใน Python

สำหรับความหลากหลายเล็กน้อยลองพิจารณาวิธีการถอดรหัสใน Python หากเรามีลูกค้า MQTT ในเครื่องเสมือนเดียวกันกับโบรกเกอร์ที่วิเคราะห์ข้อมูลหรือเก็บไว้ในฐานข้อมูล ให้สมมติว่าคุณได้รับข้อมูลเป็นสตริง “payload” จากบางอย่างเช่นไคลเอนต์ Pho MQTT ที่ยอดเยี่ยมสำหรับ Python

ในกรณีนี้สะดวกในการเข้ารหัสข้อมูลที่เข้ารหัสบน nodemcu ก่อนส่งสัญญาณ ดังนั้นใน nodeMCU เราแปลงข้อมูลที่เข้ารหัสทั้งหมดเป็น hex ตัวอย่างเช่น: Encrypted_iv = crypto.toHex (crypto.encrypt (“AES-CBC”, Ivkey, IV, Staticiv))

การเผยแพร่ Sessionized สุ่มไม่ได้กล่าวถึงด้านล่าง แต่ง่ายพอที่จะใช้ OS.urandom () และไคลเอนต์ Paho MQTT การถอดรหัสได้รับการจัดการดังต่อไปนี้:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
จาก Crypto.cipher นำเข้า AES
นำเข้า binascii
จาก crypto.hash นำเข้า Sha, HMAC

# กำหนดคีย์ทั้งหมด
ivkey = ‘2345678901Abcdef’
Datakey = ‘0123456789ABCDEF’
staticiv = ‘ABCDEF2345678901’
POSTPRASE = ‘MyPassphrase’

# แปลงสตริงที่ได้รับเป็นรายการ
data = payload.split (& quot; & quot;)

# แยกรายการรายการ
Encrypted_iv = binascii.unhexlify (ข้อมูล [0])
Encrypted_nodeid = binascii.unhexlify (ข้อมูล [1])
Encrypted_Data = binascii.unhexlify (ข้อมูล [2])
ได้รับ _Hash = binascii.unhexlify (ข้อมูล [3])

# ถอดรหัสเวกเตอร์การเริ่มต้น
iv_decryption_suite = aes.new (ivkey, aes.mode_cbc, staticiv)
iv = iv_decryption_suite.decrypt (Encrypted_iv)

# ถอดรหัสข้อมูลโดยใช้เวกเตอร์การเริ่มต้น
ID_DECryption_Suite = AES.New (Datakey, AES.mode_cbc, IV)
nodeid = id_decryption_suite.decrypt (encrypted_nodeid)
data_decryption_suite = aes.new (datakey, aes.mode_cbc, iv)
Sensordata = data_decryption_suite.decrypt (Encrypted_data)

# คำนวณฟังก์ชั่นแฮชเพื่อเปรียบเทียบกับ GEADERS_HASH
fullmessage = s.join ([nodeid, iv, sensordata, sessionid])
HMAC = HMAC.New (POSTPRASE, FULLMESSAG, SHA)
computed_hash = hmac.hexdigest ()

# ดู docs.python.org/2/library/hmac.html สำหรับวิธีการเปรียบเทียบแฮชอย่างปลอดภัย

จุดสิ้นสุดการเริ่มต้น

ตอนนี้เรามีระบบที่ส่งข้อความที่เข้ารหัสและรับรองความถูกต้องผ่านเซิร์ฟเวอร์ MQTT ไปยังไคลเอนต์ ESP8266 อื่นหรือระบบขนาดใหญ่ที่ใช้ Python ยังมีปลายหลวมที่สำคัญสำหรับคุณที่จะผูกหากคุณใช้สิ่งนี้ด้วยตัวเอง กุญแจทั้งหมดที่เก็บไว้ในหน่วยความจำแฟลช ESP8266S ดังนั้นคุณจะต้องควบคุมการเข้าถึงอุปกรณ์เหล่านี้เพื่อป้องกันไม่ให้วิศวกรรมย้อนกลับ ปุ่มนี้จะถูกเก็บไว้ในรหัสคอมพิวเตอร์ที่ได้รับข้อมูลที่นี่เรียกใช้ Python นอกจากนี้คุณอาจต้องการให้ลูกค้าแต่ละรายมีคีย์และรหัสผ่านที่แตกต่างกัน นั่นเป็นวัสดุลับจำนวนมากเพื่อให้การอัปเดตที่ปลอดภัยและอาจเกิดขึ้นเมื่อจำเป็น การแก้ปัญหาการกระจายกุญแจจะถูกทิ้งให้เป็นแบบฝึกหัดสำหรับผู้อ่านที่มีแรงจูงใจ

และในบันทึกการปิดหนึ่งในสิ่งที่น่ากลัวเกี่ยวกับการเขียนบทความที่เกี่ยวข้องกับการเข้ารหัสคือความเป็นไปได้ที่จะผิดบนอินเทอร์เน็ต นี่เป็นแอปพลิเคชั่นที่ค่อนข้างตรงไปตรงมาของโหมด AES-CBC ที่ผ่านการทดสอบและจริงกับ HMAC ดังนั้นจึงควรเป็นของแข็งที่ค่อนข้างแข็ง อย่างไรก็ตามหากคุณพบข้อบกพร่องที่น่าสนใจในด้านบนโปรดแจ้งให้เราทราบในความคิดเห็น

Author: found

Leave a Reply

Your email address will not be published. Required fields are marked *