ฉันจะรักษาความปลอดภัยการเรียก REST API ได้อย่างไร


92

ฉันกำลังพัฒนาเว็บแอปพักผ่อนที่ใช้กรอบเว็บยอดนิยมในแบ็กเอนด์พูด (ราง, ซินาตร้า, กระติกน้ำ, express.js) ตามหลักการแล้วฉันต้องการพัฒนาฝั่งไคลเอ็นต์ด้วย Backbone.js ฉันจะให้เฉพาะฝั่งไคลเอ็นต์ JavaScript ของฉันโต้ตอบกับการเรียก API เหล่านั้นได้อย่างไร ฉันไม่ต้องการให้การเรียก API เหล่านั้นเป็นสาธารณะและถูกเรียกโดยcurlหรือเพียงแค่ป้อนลิงก์บนเบราว์เซอร์


การเรียก API ทั้งหมดของคุณต้องการโทเค็นที่ส่งไปยังไคลเอนต์เมื่อเพจของคุณได้รับการบริการหรือไม่?
hajpoj

ดูเพิ่มเติมREST ตรวจสอบและเปิดเผย API ที่สำคัญ
Arjan

Amazon AWS javascript SDK ใช้ URL ของออบเจ็กต์ที่ลงนามล่วงหน้า: - docs.aws.amazon.com/AmazonS3/latest/dev/…
rjha94

คำตอบ:


91

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

ที่กล่าวว่าหากฉันอ่านคำถามของคุณอย่างถูกต้องนี่ไม่ใช่สิ่งที่คุณต้องการหลีกเลี่ยง: สิ่งที่คุณไม่ต้องการให้เกิดขึ้นจริงๆคือ API ของคุณถูกใช้ไป (เป็นประจำ) โดยที่ไคลเอ็นต์ JS ของคุณไม่เกี่ยวข้อง ต่อไปนี้เป็นแนวคิดบางประการเกี่ยวกับวิธีการหากไม่มีการบังคับใช้อย่างน้อยก็แนะนำให้ใช้ไคลเอนต์ของคุณ:

  • ฉันแน่ใจว่า API ของคุณมีฟิลด์การตรวจสอบสิทธิ์บางประเภท (เช่นแฮชคำนวณจากไคลเอนต์) ถ้าไม่ลองดูคำถาม SOนี้ ตรวจสอบให้แน่ใจว่าคุณใช้ salt (หรือแม้แต่คีย์ API) ที่มอบให้กับไคลเอ็นต์ JS ของคุณตามเซสชัน (aot hardcoded) ด้วยวิธีนี้ผู้ใช้ API ที่ไม่ได้รับอนุญาตจะถูกบังคับให้ทำงานมากขึ้น

  • ในการโหลดไคลเอ็นต์ JS โปรดจำส่วนหัว HTTP บางส่วน (ตัวแทนผู้ใช้อยู่ในใจ) และที่อยู่ IP และขอการตรวจสอบสิทธิ์อีกครั้งหากมีการเปลี่ยนแปลงโดยใช้บัญชีดำสำหรับผู้ต้องสงสัยตามปกติ นี่เป็นการบังคับให้ผู้โจมตีทำการบ้านอย่างละเอียดอีกครั้ง

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

ฉันอาจจะไม่ได้พูดอย่างนั้นด้วยความชัดเจนที่จำเป็น: ฉันคิดว่ามันเป็นไปไม่ได้ที่จะทำให้เป็นไปไม่ได้เลยที่ผู้ใช้บริการจะใช้บริการของคุณ แต่คุณสามารถทำให้มันยากได้ก็อาจไม่คุ้มกับความยุ่งยาก


นี่เป็นข้อมูลที่เป็นประโยชน์ แต่ถ้าฉันต้องการทำการรับรองความถูกต้องจากแบ็กเอนด์ api ไปยังแอพ api อื่นเช่นเซิร์ฟเวอร์แยกต่างหากเพื่อให้คำถามของฉันง่ายขึ้นฉันต้องการให้ back-end aka node.js ส่งคำขอดึงข้อมูลไปยังแบ็กเอนด์อื่น end server ซึ่งเป็นของฉันเองด้วยเหตุผลบางประการที่จำเป็น แต่ฉันต้องการรักษาความปลอดภัยการโทร api เนื่องจากสามารถเข้าถึงข้อมูลที่ละเอียดอ่อนได้และฉันไม่สามารถใช้ sesions หรือ jwt ได้เพราะฉันไม่สามารถจัดเก็บไว้ในเบราว์เซอร์ได้
พีระมิด

@Thepyramid ไม่สำคัญว่าการเรียก API ทำอะไรที่ฝั่งเซิร์ฟเวอร์โดยเฉพาะอย่างยิ่งถ้าฝั่งเซิร์ฟเวอร์ทำการเรียก API ระดับที่ 2 อีกครั้ง ส่วนสำคัญคือการปฏิบัติต่อเซิร์ฟเวอร์ของคุณไม่ใช่ในฐานะพร็อกซี แต่เป็นแอปพลิเคชัน
Eugen Rieck

คุณช่วยอธิบายเพิ่มเติมเกี่ยวกับการสร้างเป็นแอปพลิเคชันที่ไม่ใช่พร็อกซีได้หรือไม่
พีระมิด

1
สิ่งที่ฉันหมายถึงคือ: เพื่อให้ได้รับความปลอดภัยในระดับที่เหมาะสมคุณต้องใช้เครื่องมือทั้งหมดที่ webapp มี: เซสชัน, ฐานข้อมูลการตรวจสอบสิทธิ์, ตรรกะทางธุรกิจ หากไม่ทำเช่นนั้นและเพียงแค่ปฏิบัติต่อเซิร์ฟเวอร์ของคุณเป็นวิธีส่งคำขอไปยังเซิร์ฟเวอร์อื่นคุณเพียงแค่ใช้เป็นพร็อกซีสำหรับเซิร์ฟเวอร์อื่นนั้นและถูก จำกัด ด้วยความปลอดภัยใด ๆ ที่เซิร์ฟเวอร์อื่นเสนอ
Eugen Rieck

1
@PirateApp ผู้โจมตีสามารถเพิกเฉยต่อส่วนหัว CSRF ได้อย่างง่ายดาย ใช้งานได้เฉพาะในกรณีที่อุปกรณ์ปลายทางเป็นเบราว์เซอร์ที่ไม่ได้จับคู่
Eugen Rieck

12

คุณควรใช้ระบบตรวจสอบสิทธิ์บางประเภท วิธีที่ดีวิธีหนึ่งในการจัดการกับปัญหานี้คือการกำหนดตัวแปรส่วนหัวที่คาดไว้ ตัวอย่างเช่นคุณสามารถเรียกใช้ auth / login API ที่ส่งคืนโทเค็นเซสชัน การเรียก API ของคุณในภายหลังจะคาดว่าจะมีการตั้งค่าโทเค็นเซสชันในตัวแปรส่วนหัว HTTP ที่มีชื่อเฉพาะเช่น "your-api-token"

ระบบอื่น ๆ อีกมากมายจะสร้างโทเค็นการเข้าถึงหรือคีย์ที่คาดหวัง (เช่น youtube, facebook หรือ twitter) โดยใช้ระบบบัญชี api บางประเภท ในกรณีดังกล่าวลูกค้าของคุณจะต้องจัดเก็บสิ่งเหล่านี้ไว้ในไคลเอนต์

จากนั้นก็เป็นเพียงเรื่องของการเพิ่มการตรวจสอบเซสชันลงในกรอบงาน REST ของคุณและทิ้งข้อยกเว้น ถ้าเป็นไปได้รหัสสถานะ (นิ่งเฉย) จะเป็นข้อผิดพลาด 401


8
แม้ว่าจะไม่มีอะไรหยุดพวกเขาไม่ให้มองไปที่ส่วนหัวและสร้างซ้ำ
cdmckay

1
@cdmckay - โทเค็นต้องตรงกับโทเค็นที่เก็บไว้ในเซสชัน เพียงแค่สร้างส่วนหัวซ้ำจะทำให้เกิดการตอบสนอง "ไม่ได้รับอนุญาต" หากมาจากเซสชันอื่น
Andrei Volgin

3
พวกเขายังคงสามารถใช้เซสชันเดิมและแก้ไขคำขอก่อนที่จะส่งไปยัง API แม้ว่า ... หรือแม้กระทั่งการใช้คอนโซลที่รันไทม์ก็สร้างการโทรที่มีส่วนหัว / ฟิลด์ที่ตรงกันซึ่งแก้ไขเฉพาะส่วนที่คุณต้องการ ...
Potter Rafed

2
@PotterRafed: หากผู้ใช้เข้าถึงเซสชันที่ถูกต้องของตนเองซึ่งเรียกว่าการใช้แอปไม่ใช่การโจมตี วัตถุประสงค์ของการตรวจสอบสิทธิ์คือเพื่อป้องกันการเข้าถึงเซสชัน / ข้อมูลของผู้ใช้รายอื่น
Andrei Volgin

@AndreiVolgin ใช่พอสมควร แต่ก็ยังมีช่องโหว่
Potter Rafed

9

ปัจจุบันมีมาตรฐานแบบเปิดที่เรียกว่า "JSON Web Token"

ดูhttps://jwt.io/ & https://en.wikipedia.org/wiki/JSON_Web_Token

JSON Web Token (JWT) เป็นมาตรฐานแบบเปิดที่ใช้ JSON (RFC 7519) สำหรับการสร้างโทเค็นที่ยืนยันการอ้างสิทธิ์จำนวนหนึ่ง ตัวอย่างเช่นเซิร์ฟเวอร์สามารถสร้างโทเค็นที่มีการอ้างสิทธิ์ "เข้าสู่ระบบในฐานะผู้ดูแลระบบ" และส่งมอบให้กับลูกค้า จากนั้นไคลเอนต์สามารถใช้โทเค็นนั้นเพื่อพิสูจน์ว่าพวกเขาเข้าสู่ระบบในฐานะผู้ดูแลระบบ โทเค็นลงนามโดยคีย์ของเซิร์ฟเวอร์ดังนั้นเซิร์ฟเวอร์จึงสามารถตรวจสอบได้ว่าโทเค็นนั้นถูกต้องตามกฎหมาย โทเค็นได้รับการออกแบบให้มีขนาดกะทัดรัดปลอดภัย URL และใช้งานได้โดยเฉพาะในบริบทการลงชื่อเพียงครั้งเดียว (SSO) ของเว็บเบราว์เซอร์ โดยทั่วไปการอ้างสิทธิ์ JWT สามารถใช้เพื่อส่งต่อข้อมูลประจำตัวของผู้ใช้ที่ได้รับการพิสูจน์ตัวตนระหว่างผู้ให้บริการข้อมูลประจำตัวและผู้ให้บริการหรือการอ้างสิทธิ์ประเภทอื่น ๆ ตามที่กระบวนการทางธุรกิจกำหนด [1] [2] โทเค็นยังสามารถรับรองความถูกต้องและเข้ารหัสได้ [3] [4]


อะไรที่จะป้องกันไม่ให้ผู้ใช้คัดลอกโทเค็นและใช้ในการตอบสนองอื่น ๆ
Ulad Kasach

1
@UladKasach พูดตามตรงฉันไม่เคยเจาะลึกเข้าไปในพวกเขา แต่ afaik มันหมดอายุไม่ซ้ำใครสำหรับผู้ใช้ของคุณและเข้ารหัสด้วย SSL (ซึ่งแน่นอนว่าคุณกำลังฝึกฝนอยู่) มันเป็นความคิดเดียวกันกับที่อยู่เบื้องหลัง oauth afaik
bbozo

3

ขอโทษนะ @MarkAmery และ Eugene แต่ไม่ถูกต้อง

แอป js + html (ไคลเอนต์) ของคุณที่ทำงานในเบราว์เซอร์สามารถตั้งค่าเพื่อยกเว้นการเรียกโดยตรงที่ไม่ได้รับอนุญาตไปยัง API ดังนี้:

  1. ขั้นตอนแรก: ตั้งค่า API เพื่อต้องการการตรวจสอบสิทธิ์ ลูกค้าต้องตรวจสอบตัวเองผ่านทางเซิร์ฟเวอร์ (หรือบางเซิร์ฟเวอร์ความปลอดภัยอื่น ๆ )เช่นขอให้ผู้ใช้ของมนุษย์ที่จะให้รหัสผ่านถูกต้อง

ก่อนการตรวจสอบสิทธิ์จะไม่ยอมรับการเรียกไปยัง API

ในระหว่างการตรวจสอบสิทธิ์ "โทเค็น" จะถูกส่งกลับ

หลังจากตรวจสอบสิทธิ์แล้วเฉพาะการเรียก API ที่มี "โทเค็น" เท่านั้นที่จะได้รับการยอมรับ

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

  1. ขั้นตอนที่สอง: ตั้งค่า API ความปลอดภัยเพิ่มเติมซึ่งจะถูกเรียกใช้ภายในระยะเวลาสั้น ๆ หลังจากที่ไคลเอนต์ js + html app ถูกร้องขอจากเซิร์ฟเวอร์ในตอนแรก "โทรกลับ" นี้จะบอกเซิร์ฟเวอร์ว่าดาวน์โหลดไคลเอนต์สำเร็จแล้ว จำกัด การเรียก REST API ของคุณให้ทำงานเฉพาะในกรณีที่ไคลเอ็นต์ถูกร้องขอเมื่อเร็ว ๆ นี้และประสบความสำเร็จ

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

คุณจึงไม่ต้องกังวลว่านี่อาจเป็นผู้ใช้ที่ไม่ได้รับอนุญาตโดยไม่มีข้อมูลรับรอง

(ชื่อคำถาม 'ฉันจะรักษาความปลอดภัยการเรียก REST API ได้อย่างไร' และจากสิ่งที่คุณพูดส่วนใหญ่นั่นเป็นข้อกังวลหลักของคุณไม่ใช่คำถามตามตัวอักษรว่า API ของคุณถูกเรียกอย่างไร แต่ถูกต้องหรือไม่ )


5
จุดที่สองไม่สมเหตุสมผล หากผู้โจมตีต้องการโหลดแอปของคุณเขาจะทำ (การโทรกลับของคุณจะปรากฏให้เห็น) แล้วโจมตี.
Andrei Volgin

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

คุณสามารถอนุญาตคำขอจากโดเมนของคุณเท่านั้น
Andrei Volgin

ซึ่งจะ จำกัด เฉพาะการเรียกภายในโดเมนดังนั้นตอนนี้ผู้ใช้แอปเบราว์เซอร์จาวาสคริปต์จะต้องอยู่ในโดเมน (อาจไม่ใช่สิ่งที่ knd ต้องการ) และผู้ใช้เหล่านั้นยังสามารถเรียก API ได้โดยตรงผ่าน curl
pashute

2
สิ่งที่คุณดูเหมือนจะมองข้ามคือสิ่งที่คุณขอให้เบราว์เซอร์ของผู้ใช้ทำนั้นผู้โจมตีสามารถจำลองแบบได้ - เพื่อให้เบราว์เซอร์ทำงานได้นั้นจะต้องอ่านได้
Eugen Rieck

1
  1. ตั้งค่า SESSION var บนเซิร์ฟเวอร์เมื่อไคลเอนต์โหลดindex.html(หรือbackbone.jsฯลฯ ) ของคุณเป็นครั้งแรก

  2. ตรวจสอบตัวแปรนี้ทางฝั่งเซิร์ฟเวอร์ทุกครั้งที่เรียก API

ปล. นี่ไม่ใช่วิธีแก้ "ความปลอดภัย" !!! นี่เป็นเพียงการลดการโหลดบนเซิร์ฟเวอร์ของคุณเพื่อไม่ให้ผู้อื่นละเมิดหรือ "hotlink" API ของคุณจากเว็บไซต์และแอปอื่น ๆ


0

นี่คือสิ่งที่ฉันทำ:

  1. รักษาความปลอดภัย API ด้วย HTTP Header ด้วยการเรียกเช่น X-APITOKEN:

  2. ใช้ตัวแปรเซสชันใน PHP มีระบบล็อกอินและบันทึกโทเค็นผู้ใช้ในตัวแปรเซสชัน

  3. เรียกรหัส JS ด้วย Ajax เป็น PHP และใช้ตัวแปรเซสชันกับ curl เพื่อเรียก API ด้วยวิธีนี้หากไม่ได้ตั้งค่าตัวแปรเซสชันมันจะไม่เรียกและโค้ด PHP มีโทเค็นการเข้าถึงไปยัง API

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.